diff -Nru sarrac-2.19.06b3/debian/changelog sarrac-2.19.07b1ubuntu1/debian/changelog --- sarrac-2.19.06b3/debian/changelog 2019-06-18 05:52:15.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/debian/changelog 2019-07-22 15:00:43.000000000 +0000 @@ -1,14 +1,20 @@ -sarrac (2.19.06b3-0~ubuntu18.10.1) cosmic; urgency=low +sarrac (2.19.07b1ubuntu1-0~ubuntu18.10.1) cosmic; urgency=low * Auto build. - -- Supercomputing @ Government of Canada Tue, 18 Jun 2019 05:52:15 +0000 + -- Supercomputing @ Government of Canada Mon, 22 Jul 2019 15:00:43 +0000 + +sarrac (2.19.07b1ubuntu1) unstable; urgency=medium + + * bugfix #39, add v03 posting (only, cannot parse) + + -- Peter Silva Mon, 22 Jul 2019 09:05:36 -0400 sarrac (2.19.06b3) unstable; urgency=medium * bugfix: issue #61 - support for vip option. - -- Peter Silva Tue, 18 Jun 2019 01:43:10 -0400 + -- Peter Silva Tue, 18 Jun 2019 01:43:10 -0400 sarrac (2.19.06b2) unstable; urgency=medium diff -Nru sarrac-2.19.06b3/debian/git-build-recipe.manifest sarrac-2.19.07b1ubuntu1/debian/git-build-recipe.manifest --- sarrac-2.19.06b3/debian/git-build-recipe.manifest 2019-06-18 05:52:15.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/debian/git-build-recipe.manifest 2019-07-22 15:00:43.000000000 +0000 @@ -1,2 +1,2 @@ # git-build-recipe format 0.4 deb-version {debupstream}-0 -lp:~ssc-hpc-chp-spc/metpx-sarrac/+git/master git-commit:9dfd2e0fdfe57e5b26f4318b8f47a0a272f3a024 +lp:~ssc-hpc-chp-spc/metpx-sarrac/+git/master git-commit:eec0794a6a73558f9971feed3c14f09d3f3e1fd2 diff -Nru sarrac-2.19.06b3/local_post.conf sarrac-2.19.07b1ubuntu1/local_post.conf --- sarrac-2.19.06b3/local_post.conf 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/local_post.conf 2019-07-22 15:00:43.000000000 +0000 @@ -1,9 +1,12 @@ post_broker amqp://tsource@localhost vip 127.0.0.1 +#blocksize 1024 realpath -loglevel debug -debug True +#loglevel debug +#debug True +sum s shim_post_minterval 10 +post_topic_prefix v03.post #shim_skip_parent_open_files #shim_post_once shim_defer_posting_to_exit @@ -12,10 +15,10 @@ header toto=pig events modify,link,delete post_exchange xs_tsource_cpost_watch -post_base_dir / -post_base_url sftp://sarra_test@localhost +#post_base_dir / +post_base_url sftp://peter@localhost -path test_dir +#path test_dir #reject ${HOSTNAME}.*SBAND.* #reject .*${HOHOHO}.* #accept .*XBAND.*${CONFIG} diff -Nru sarrac-2.19.06b3/sr_config.c sarrac-2.19.07b1ubuntu1/sr_config.c --- sarrac-2.19.06b3/sr_config.c 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_config.c 2019-07-22 15:00:43.000000000 +0000 @@ -822,6 +822,10 @@ argument = NULL; retval = 2; + } else if (!strcmp(option, "post_topic_prefix") || !strcmp(option, "ptp" ) ) { + strcpy( sr_cfg->post_topic_prefix, argument ); + retval = 2; + } else if (!strcmp(option, "durable")) { val = StringIsTrue(argument); sr_cfg->durable = val & 2; @@ -1293,6 +1297,7 @@ sr_cfg->to = NULL; sr_cfg->user_headers = NULL; strcpy(sr_cfg->topic_prefix, "v02.post"); + strcpy(sr_cfg->post_topic_prefix, "v02.post"); sr_cfg->topics = NULL; sr_cfg->post_base_url = NULL; @@ -1615,9 +1620,9 @@ sr_cfg->message_ttl, sr_cfg->post_exchange, sr_cfg->post_exchange_split, sr_cfg->post_exchange_suffix); log_msg(LOG_DEBUG, - "\tsource=%s to=%s post_base_url=%s topic_prefix=%s pid=%d\n", + "\tsource=%s to=%s post_base_url=%s topic_prefix=%s post_topic_prefix=%s, pid=%d\n", sr_cfg->source, sr_cfg->to, sr_cfg->post_base_url, - sr_cfg->topic_prefix, sr_cfg->pid); + sr_cfg->topic_prefix, sr_cfg->post_topic_prefix, sr_cfg->pid); } // FIXME: Missing: topics, user_headers, if (!strcmp(sr_cfg->progname, "post") diff -Nru sarrac-2.19.06b3/sr_config.h sarrac-2.19.07b1ubuntu1/sr_config.h --- sarrac-2.19.06b3/sr_config.h 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_config.h 2019-07-22 15:00:43.000000000 +0000 @@ -220,7 +220,8 @@ char *to; /**< indicates destination cluster(s) for a post.*/ struct sr_topic_t *topics; /**< list of sub-topics to subscribe to.*/ - char topic_prefix[AMQP_MAX_SS]; /**< the topic prefix to either subscribe or post to.*/ + char topic_prefix[AMQP_MAX_SS]; /**< the topic prefix to subscribe to.*/ + char post_topic_prefix[AMQP_MAX_SS]; /**< the topic prefix to post to.*/ struct sr_header_t *user_headers; /**< list of arbitrary user headers for extensions and upward compatibility.*/ char *vip; /**< virtual ip address ... only act, if host has this address.*/ diff -Nru sarrac-2.19.06b3/sr_cpost.c sarrac-2.19.07b1ubuntu1/sr_cpost.c --- sarrac-2.19.06b3/sr_cpost.c 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_cpost.c 2019-07-22 15:00:43.000000000 +0000 @@ -664,7 +664,7 @@ fprintf(stderr, "\tsuppress_duplicates|sd|cache|caching (default: off)\n"); fprintf(stderr, "\t\tsuppress duplicate announcements < *cache* seconds apart. \"on\" means 15 minute caching (on=900).\n"); - fprintf(stderr, "\ttopic_prefix - AMQP topic prefix (default: v02.post )\n"); + fprintf(stderr, "\tpost_topic_prefix - AMQP topic prefix (default: v02.post )\n"); fprintf(stderr, "\tto - clusters pump network should forward to (default: broker).\n"); fprintf(stderr, diff -Nru sarrac-2.19.06b3/sr_post.c sarrac-2.19.07b1ubuntu1/sr_post.c --- sarrac-2.19.06b3/sr_post.c 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_post.c 2019-07-22 15:00:43.000000000 +0000 @@ -191,12 +191,81 @@ } +char *hex_to_b64str( char *hextr, int hexstrlen ) { + + return( "not implemented" ); +} + +const char *sum2integrity( char sum ) +{ + switch (sum) { + case '0': return( "random" ); + case 'a': return( "arbitrary" ); + case 'd': return( "md5" ); + case 'n': return( "md5name" ); + case 's': return( "sha512" ); + case 'L': return( "link" ); + case 'R': return( "remove" ); + case 'z': return( "cod" ); + default: return( "unknown" ); + } + +} + +char *v03integrity( struct sr_message_t *m ) +{ + static char istr[1024]; + const char *value; + + switch (m->sum[0]) { + case 'd' : case 'n' : case 's' : case 'L' : case 'R' : value = hex2base64( &(m->sum[2]) ); break; + case 'z' : value = sum2integrity(m->sum[2]); break; + case '0' : case 'a' : default : value = &(m->sum[2]); break; + } + sprintf( istr, " \"method\" : \"%s\", \"value\" : \"%s\" ", sum2integrity( m->sum[0] ), value ); + return(istr); + +} + +char *v03time( char *v02time ) +{ + static char buf[128]; + + strncpy( buf, v02time, 8 ); + buf[8]='T'; + buf[9]='\0'; + strcat( buf, v02time+8 ); + return(buf); +} + +/* + * return cc string with tag value pair appended in JSON ( "tag" : "value" ) + * separator is hard-coded here (beginning of sprintf.) + * FIXME: dumps core whenever this is used... something to fix. + */ +char *v03amqp_header_add( char* c, const char* tag, const char *value ) +{ + int status; + + /* check utf8 compliance of tag and value for message headers */ + if (!is_utf8(tag) || !is_utf8(value)) { + log_msg(LOG_ERROR, + "amqp header (tag, value)<>(%s,%s) not utf8 encoded, ignoring header\n", + tag, value); + } else { + status = sprintf( c, ", \"%s\" : \"%s\"", tag, value ); + c += status ; + } + return(c); +} + void sr_post_message(struct sr_context *sr_c, struct sr_message_t *m) { char fn[PATH_MAXNUL]; - char message_body[1024]; + char message_body[1024*1024]; char smallbuf[256]; char thisexchange[256]; + char sep[8]; char *c, *d; amqp_table_t table; amqp_basic_properties_t props; @@ -233,70 +302,184 @@ } // resume posting while (1) { - strcpy(message_body, m->datestamp); - strcat(message_body, " "); - strcat(message_body, m->url); - strcat(message_body, " "); - strcat(message_body, fn); - strcat(message_body, " \n"); - - header_reset(); - - if (sr_c->cfg->strip > 0) - amqp_header_add("rename", m->rename); - - if (m->from_cluster && m->from_cluster[0]) - amqp_header_add("from_cluster", m->from_cluster); - - if ((m->sum[0] != 'R') && (m->sum[0] != 'L')) { - amqp_header_add("parts", sr_message_partstr(m)); - - if (m->atime && m->atime[0]) - amqp_header_add("atime", m->atime); - - if (m->mode > 0) { - sprintf(smallbuf, "%04o", m->mode); - amqp_header_add("mode", smallbuf); - } - - if (m->mtime && m->mtime[0]) - amqp_header_add("mtime", m->mtime); - } - - if (m->sum[0] == 'L') { - amqp_header_add("link", m->link); - } - - amqp_header_add("sum", m->sum); - - if (m->to_clusters && m->to_clusters[0]) - amqp_header_add("to_clusters", m->to_clusters); - - for (uh = m->user_headers; uh; uh = uh->next) - amqp_header_add(uh->key, uh->value); - - table.num_entries = hdrcnt; - table.entries = headers; - - props._flags = - AMQP_BASIC_HEADERS_FLAG | AMQP_BASIC_CONTENT_TYPE_FLAG | - AMQP_BASIC_DELIVERY_MODE_FLAG; - props.content_type = amqp_cstring_bytes("text/plain"); - props.delivery_mode = 2; /* persistent delivery mode */ - props.headers = table; - - strcpy(thisexchange, sr_c->cfg->post_broker->exchange); + if ( !strncmp("v02.", sr_c->cfg->post_topic_prefix, 4 ) ) { + strcpy(message_body, m->datestamp); + strcat(message_body, " "); + strcat(message_body, m->url); + strcat(message_body, " "); + strcat(message_body, fn); + strcat(message_body, " \n"); + + header_reset(); + + if (sr_c->cfg->strip > 0) + amqp_header_add("rename", m->rename); + + if (m->from_cluster && m->from_cluster[0]) + amqp_header_add("from_cluster", m->from_cluster); + + if ((m->sum[0] != 'R') && (m->sum[0] != 'L')) { + amqp_header_add("parts", sr_message_partstr(m)); + + if (m->atime && m->atime[0]) + amqp_header_add("atime", m->atime); + + if (m->mode > 0) { + sprintf(smallbuf, "%03o", m->mode); + amqp_header_add("mode", smallbuf); + } + + if (m->mtime && m->mtime[0]) + amqp_header_add("mtime", m->mtime); + } + + if (m->sum[0] == 'L') { + amqp_header_add("link", m->link); + } + + amqp_header_add("sum", m->sum); + + if (m->to_clusters && m->to_clusters[0]) + amqp_header_add("to_clusters", m->to_clusters); + + for (uh = m->user_headers; uh; uh = uh->next) + amqp_header_add(uh->key, uh->value); + + table.num_entries = hdrcnt; + table.entries = headers; + + props._flags = + AMQP_BASIC_CONTENT_ENCODING_FLAG | AMQP_BASIC_HEADERS_FLAG | + AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG ; + props.content_encoding = amqp_cstring_bytes("utf-8"); + props.content_type = amqp_cstring_bytes("text/plain"); + props.delivery_mode = 2; /* persistent delivery mode */ + props.headers = table; + + strcpy(thisexchange, sr_c->cfg->post_broker->exchange); + + if (sr_c->cfg->post_broker->exchange_split > 0) { + sprintf(strchr(thisexchange, '\0'), "%02d", + m->sum[get_sumhashlen(m->sum[0]) - + 1] % sr_c->cfg->post_broker->exchange_split); + } + status = + amqp_basic_publish(sr_c->cfg->post_broker->conn, 1, + amqp_cstring_bytes(thisexchange), + amqp_cstring_bytes(m->routing_key), 0, 0, + &props, amqp_cstring_bytes(message_body)); + } else { /* v03 */ - if (sr_c->cfg->post_broker->exchange_split > 0) { - sprintf(strchr(thisexchange, '\0'), "%02d", - m->sum[get_sumhashlen(m->sum[0]) - - 1] % sr_c->cfg->post_broker->exchange_split); - } - status = - amqp_basic_publish(sr_c->cfg->post_broker->conn, 1, + // convert routing key, if necessary. + // FIXME: a generic conversion replacing topic_prefix by post_topic_prefix + // would be much better, but this >99% answer is good enough for now. + if ( m->routing_key[2] != '3' ) + m->routing_key[2] = '3' ; + + strcpy( message_body, "{" ); + c = message_body+1; + + strncpy( sep, "\n\t", 8 ); + strncpy( sep, " ", 8 ); + + status = sprintf( c, "%s\"pubTime\" : \"%s\"", sep, v03time( m->datestamp ) ); + c += status ; + status = sprintf( c, ",%s\"baseUrl\" : \"%s\"", sep, m->url ); + c += status ; + + //FIXME: after the first one, all of them start with comma, so could + // use the v03amqp_header_add routine, but it dumps core... + //c = v03amqp_header_add( c, "baseUrl", m->url ); + + status = sprintf( c, ",%s\"relPath\" : \"%s\"", sep, m->path ); + c += status ; + + status = sprintf( c, ",%s\"integrity\" : { %s }", sep, v03integrity(m) ); + c += status ; + + if (sr_c->cfg->strip > 0) { + status = sprintf( c, ",%s\"rename\" : \"%s\"", sep, m->rename ); + c += status ; + } + + if (m->from_cluster && m->from_cluster[0]) { + status = sprintf( c, ",%s\"from_cluster\" : \"%s\"", sep, m->from_cluster ); + c += status ; + } + + if (m->to_clusters) { + status = sprintf( c, ",%s\"to_clusters\" : \"%s\"", sep, m->to_clusters ); + c += status ; + } + if ((m->sum[0] != 'R') && (m->sum[0] != 'L')) { + if ( m->parts_s != '1' ) { + status = sprintf( c, ",%s\"blocks\" : { ", sep); + c += status ; + status = sprintf( c, "\"method\" : \"%s\", ", (m->parts_s=='i')?"inplace":"partitioned" ); + c += status ; + status = sprintf( c, "\"size\" : \"%ld\", ", m->parts_blksz ); + c += status ; + status = sprintf( c, "\"count\" : \"%ld\", ", m->parts_blkcount ); + c += status ; + status = sprintf( c, "\"remainder\" : \"%ld\", ", m->parts_rem ); + c += status ; + status = sprintf( c, "\"number\" : \"%ld\" }", m->parts_num ); + c += status ; + } else { + status = sprintf( c, ",%s\"size\" : \"%ld\"", sep, m->parts_blksz ); + c += status ; + } + if (m->atime && m->atime[0]) { + status = sprintf( c, ",%s\"atime\" : \"%s\"", sep, v03time( m->atime ) ); + c += status ; + } + + if (m->mode > 0) { + status = sprintf( c, ",%s\"mode\" : \"%03o\"", sep, m->mode ); + c += status ; + } + + if (m->mtime && m->mtime[0]) { + status = sprintf( c, ",%s\"mtime\" : \"%s\"", sep, v03time( m->mtime ) ); + c += status ; + } + } + + if (m->sum[0] == 'L') { + status = sprintf( c, ",%s\"link\" : \"%s\"", sep, m->link ); + c += status ; + } + + for (uh = m->user_headers; uh; uh = uh->next) { + status = sprintf( c, ",%s\"%s\" : \"%s\"", sep, uh->key, uh->value ); + c += status ; + } + + sprintf( c, "%s} \n", sep ); + c += status ; + //strcat( message_body, " } \n" ); + + log_msg( LOG_DEBUG, "v03 body=%s\n", message_body ); + + props._flags = AMQP_BASIC_CONTENT_ENCODING_FLAG | + AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG; + props.content_encoding = amqp_cstring_bytes("utf-8"); + props.content_type = amqp_cstring_bytes("text/plain"); + props.delivery_mode = 2; /* persistent delivery mode */ + + strcpy(thisexchange, sr_c->cfg->post_broker->exchange); + + if (sr_c->cfg->post_broker->exchange_split > 0) { + sprintf(strchr(thisexchange, '\0'), "%02d", + m->sum[get_sumhashlen(m->sum[0]) - + 1] % sr_c->cfg->post_broker->exchange_split); + } + status = + amqp_basic_publish(sr_c->cfg->post_broker->conn, 1, amqp_cstring_bytes(thisexchange), amqp_cstring_bytes(m->routing_key), 0, 0, &props, amqp_cstring_bytes(message_body)); + } if (status < 0) { log_msg(LOG_ERROR, @@ -429,7 +612,7 @@ free(d); } // use tmprk variable to fix 255 AMQP_SS_LEN limit - strcpy(tmprk, sr_c->cfg->topic_prefix); + strcpy(tmprk, sr_c->cfg->post_topic_prefix); strcat(tmprk, "."); strcat(tmprk, m->path + (*(m->path) == '/')); @@ -439,7 +622,7 @@ strcpy(m->routing_key, tmprk); lasti = 0; - for (int i = strlen(sr_c->cfg->topic_prefix); i < strlen(m->routing_key); i++) { + for (int i = strlen(sr_c->cfg->post_topic_prefix); i < strlen(m->routing_key); i++) { if (m->routing_key[i] == '/') { if (lasti > 0) { m->routing_key[lasti] = '.'; diff -Nru sarrac-2.19.06b3/sr_util.c sarrac-2.19.07b1ubuntu1/sr_util.c --- sarrac-2.19.06b3/sr_util.c 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_util.c 2019-07-22 15:00:43.000000000 +0000 @@ -360,6 +360,78 @@ log_msg(LOG_DEBUG, "child daemonizing complete.\n"); } +/* v03 conversion code for base64 + */ + +char b64rep( char i ) +{ + if ( i > 64 ) fprintf( stderr, "errror in representation: %i should not be input to b64encode from hex\n", i ); + if ( i == 63 ) return( '/' ); + if ( i == 62 ) return( '+' ); + if ( i >= 52 ) return( i + '0' - 52 ); + if ( i >= 26 ) return( i + 'a' - 26 ); + return( i + 'A' ); +} + +char h2b( char i ) { + if ( i > 'f' ) fprintf( stderr, "errror in representation: %i should not be input to h2b from hex\n", i ); + if (i >= 'a' ) return ( i - 'a' + 10 ); + + if ( i > 'F' ) fprintf( stderr, "errror in representation: %i should not be input to h2b from hex\n", i ); + if (i >= 'A' ) return ( i - 'A' + 10 ); + + if ( i > '9' ) fprintf( stderr, "errror in representation: %i should not be input to h2b from hex\n", i ); + return( i - '0' ); + +} + +char *hex2base64( char *hstr ) +{ + static char buf[1024]; + int hxlen; + unsigned int h,b ; + char pad[2]; + + hxlen = strlen(hstr); + b=0; + for ( h = 0 ; h < hxlen-2 ; h+=3 ) { + + //base64 encoding requires line feed after every 76 chars... +/* + if (!((b-1)%77)) + buf[b++]='\n'; + */ +// but Sarracenia expects fake line feed... + if ( (b>10) && !((b%78)) ) + { + buf[b++]='\\'; + buf[b++]='n'; + } + + pad[0] = (h2b(hstr[h]) << 2) | ( h2b(hstr[h+1])>>2 ) ; + pad[1] = ((h2b(hstr[h+1])&0x03) << 4) | ( h2b(hstr[h+2]) ) ; + buf[b++] = b64rep(pad[0]); + buf[b++] = b64rep(pad[1]); + } + if (( hxlen - h ) >= 2 ) { + pad[0] = (h2b(hstr[h]) << 2) | ( h2b(hstr[h+1])>>2 ) ; + buf[b++] = b64rep(pad[0]); + pad[1] = ((h2b(hstr[h+1])&0x03) << 4) ; + buf[b++] = b64rep(pad[1]); + } else if (( hxlen - h ) == 1 ) { + pad[0] = (h2b(hstr[h]) << 2) ; + buf[b++] = b64rep(pad[0]); + } + while (h < hxlen) { + buf[b++] = '='; + h++; + } + buf[b]='\0'; + + return( buf ); +} + + /* size of buffer used to read the file content in calculating checksums. */ #define SUMBUFSIZE (4096*1024) diff -Nru sarrac-2.19.06b3/sr_util.h sarrac-2.19.07b1ubuntu1/sr_util.h --- sarrac-2.19.06b3/sr_util.h 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_util.h 2019-07-22 15:00:43.000000000 +0000 @@ -70,6 +70,16 @@ void daemonize(int close_stdout); // executed to go from a management instance to a daemon working instance. + +/* returns the base64 encoded string converted from a hex-encoded input + This is used to produce v03 integrity fields. + + no dynamic allocation is done, space is allocated in a static buffer, so use immediately, not thread safe. + + */ + +char *hex2base64( char *hstr ); + #define SR_TIMESTRLEN (26) // Assumed longest possible hash. typeprefix + SHA512_DIGEST diff -Nru sarrac-2.19.06b3/sr_utiltest.c sarrac-2.19.07b1ubuntu1/sr_utiltest.c --- sarrac-2.19.06b3/sr_utiltest.c 2019-06-18 05:52:14.000000000 +0000 +++ sarrac-2.19.07b1ubuntu1/sr_utiltest.c 2019-07-22 15:00:43.000000000 +0000 @@ -44,6 +44,9 @@ int testcnt = 0; int success = 0; int i = 0; + char input[1024]; + char *output; + char expected[1024]; unsigned char original_hash[SHA512_DIGEST_LENGTH + 1]; unsigned char rt_hash[SHA512_DIGEST_LENGTH + 1]; @@ -121,6 +124,49 @@ testcnt++; + log_msg( LOG_INFO, " testing hex2base64 conversion\n" ); + + strcpy( input, "d15f684279f7d4721671a587325160a8") ; + output = hex2base64( input ); + strcpy( expected, "0V9oQnn31HIWcaWHMlFgqA==" ); + printf( " input: %s\n output: %s\n", input, output ); + if ( strcmp( output, expected ) ) + { + log_msg( LOG_ERROR, "hex to base64 conversion expected: %s\n", expected ); + } else { + fprintf(stderr, "OK: as expected!\n"); + success++; + } + testcnt++; + + strcpy( input, "4c1e5d8ef96ee9e00f2feb38f70de21f") ; + output = hex2base64( input ); + printf( " input: %s\n output: %s\n", input, output ); + strcpy( expected, "TB5djvlu6eAPL+s49w3iHw==" ); + if ( strcmp( output, expected ) ) + { + log_msg( LOG_ERROR, "hex to base64 conversion expected: %s\n", expected ); + } else { + fprintf(stderr, "OK: as expected!\n"); + success++; + } + testcnt++; + + + strcpy( input, "dcc2806747cc0046f4dcd7ac93411bde896fd089edf3420e0e5d1fe0b6b876b403dfdcf7221b6dd520298bd2de4c4e74bdd0cd76c10d69ca44ebc724dedda7b1" ); + output = hex2base64( input ); + printf( " input: %s\n output: %s\n", input, output ); + strcpy( expected, "3MKAZ0fMAEb03Nesk0Eb3olv0Int80IODl0f4La4drQD39z3Ihtt1SApi9LeTE50vdDNdsENacpE68\\nck3t2nsQ==" ); + if ( strcmp( output, expected ) ) + { + log_msg( LOG_ERROR, "hex to base64 conversion expected: %s\n", expected ); + } else { + fprintf(stderr, "OK: as expected!\n"); + success++; + } + + testcnt++; + printf("%s %d/%d tests passed\n", (success >= testcnt) ? "OK" : "FAILED", success, testcnt); exit(!(success >= testcnt)); }