Back to index

opendkim  2.6.2
repute.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2011, 2012, The OpenDKIM Project.  All rights reserved.
00003 */
00004 
00005 #ifndef lint
00006 static char repute_c_id[] = "$Id$";
00007 #endif /* ! lint */
00008 
00009 #include "build-config.h"
00010 
00011 /* system includes */
00012 #include <sys/param.h>
00013 #include <sys/types.h>
00014 #include <pthread.h>
00015 #include <assert.h>
00016 #include <errno.h>
00017 #include <stdlib.h>
00018 #include <limits.h>
00019 #include <stdio.h>
00020 #include <string.h>
00021 
00022 #ifdef USE_XML2
00023 /* libxml2 includes */
00024 # include <libxml/parser.h>
00025 # include <libxml/tree.h>
00026 #endif /* USE_XML2 */
00027 
00028 #ifdef USE_JANSSON
00029 /* libjansson includes */
00030 # include <jansson.h>
00031 #endif /* USE_JANSSON */
00032 
00033 /* libcurl includes */
00034 #include <curl/curl.h>
00035 
00036 /* libut includes */
00037 #include <ut.h>
00038 
00039 /* librepute includes */
00040 #include "repute.h"
00041 
00042 /* limits */
00043 #define       REPUTE_BUFBASE       1024
00044 #define       REPUTE_URL    1024
00045 
00046 /* data types */
00047 struct repute_io
00048 {
00049        CURLcode             repute_errcode;
00050        unsigned int         repute_rcode;
00051        size_t               repute_alloc;
00052        size_t               repute_offset;
00053        char *               repute_buf;
00054        struct repute_io *   repute_next;
00055        CURL *               repute_curl;
00056 };
00057 
00058 struct repute_handle
00059 {
00060        unsigned int         rep_reporter;
00061        pthread_mutex_t             rep_lock;
00062        struct repute_io *   rep_ios;
00063        const char *         rep_server;
00064        const char *         rep_useragent;
00065        const char *         rep_curlversion;
00066        char                 rep_uritemp[REPUTE_URL + 1];
00067        char                 rep_error[REPUTE_BUFBASE + 1];
00068 };
00069 
00070 struct repute_lookup
00071 {
00072        int                  rt_code;
00073        const char *         rt_name;
00074 };
00075 
00076 /* lookup tables */
00077 struct repute_lookup repute_lookup_elements[] =
00078 {
00079        { REPUTE_XML_CODE_ASSERTION,       REPUTE_XML_ASSERTION },
00080        { REPUTE_XML_CODE_EXTENSION,       REPUTE_XML_EXTENSION },
00081        { REPUTE_XML_CODE_RATED,    REPUTE_XML_RATED },
00082        { REPUTE_XML_CODE_RATER,    REPUTE_XML_RATER },
00083        { REPUTE_XML_CODE_RATER_AUTH,      REPUTE_XML_RATER_AUTH },
00084        { REPUTE_XML_CODE_RATING,   REPUTE_XML_RATING },
00085        { REPUTE_XML_CODE_SAMPLE_SIZE,     REPUTE_XML_SAMPLE_SIZE },
00086        { REPUTE_XML_CODE_UPDATED,  REPUTE_XML_UPDATED },
00087        { REPUTE_XML_CODE_UNKNOWN,  NULL }
00088 };
00089 
00090 #ifdef USE_XML2
00091 /*
00092 **  REPUTE_LIBXML2_ERRHANDLER -- error handler function provided to libxml2
00093 **
00094 **  Parameters:
00095 **     ctx -- a "parsing" context (generally a FILE *)
00096 **     fmt -- message format
00097 **     ... -- variable arguments
00098 **
00099 **  Return value:
00100 **     None.
00101 **
00102 **  Notes:
00103 **     Oddly, libxml2 writes errors to stderr by default without a provided
00104 **     handler function.  We check for errors in other ways and this
00105 **     program typically runs as a daemon, so we'll suppress that by
00106 **     providing an error handler that does nothing.
00107 */
00108 
00109 static void
00110 repute_libxml2_errhandler(void *ctx, const char *fmt, ...)
00111 {
00112        return;
00113 }
00114 #endif /* USE_XML2 */
00115 
00116 /*
00117 **  REPUTE_CURL_WRITEDATA -- callback for libcurl to deliver data
00118 **
00119 **  Parameters:
00120 **     ptr -- pointer to the retrieved data
00121 **     size -- unit size
00122 **     nmemb -- unit count
00123 **     userdata -- opaque userdata (points to a repute_io structure)
00124 **
00125 **  Return value:
00126 **     Number of bytes taken in.  If different from "size", libcurl reports
00127 **     an error.
00128 */
00129 
00130 static size_t
00131 repute_curl_writedata(char *ptr, size_t size, size_t nmemb, void *userdata)
00132 {
00133        size_t need;
00134        struct repute_io *io;
00135 
00136        io = userdata;
00137 
00138        need = size * nmemb;
00139 
00140        if (io->repute_buf == NULL)
00141        {
00142               io->repute_alloc = MAX(REPUTE_BUFBASE, need);
00143               io->repute_buf = malloc(io->repute_alloc);
00144               if (io->repute_buf == NULL)
00145                      return 0;
00146               memset(io->repute_buf, '\0', io->repute_alloc);
00147        }
00148        else if (io->repute_offset + need > io->repute_alloc)
00149        {
00150               size_t newsize;
00151               char *newbuf;
00152 
00153               newsize = MAX(io->repute_alloc * 2, io->repute_alloc + need);
00154               newbuf = realloc(io->repute_buf, newsize);
00155               if (newbuf == NULL)
00156               {
00157                      return 0;
00158               }
00159               else
00160               {
00161                      memset(newbuf + io->repute_offset, '\0',
00162                             newsize - io->repute_offset);
00163               }
00164               io->repute_buf = newbuf;
00165               io->repute_alloc = newsize;
00166        }
00167 
00168        memcpy(io->repute_buf + io->repute_offset, ptr, need);
00169 
00170        io->repute_offset += need;
00171 
00172        return need;
00173 }
00174 
00175 /*
00176 **  REPUTE_NAME_TO_CODE -- look up a name in a table
00177 **
00178 **  Parameters:
00179 **     tbl -- table to search
00180 **     name -- name to find
00181 **
00182 **  Return value:
00183 **     Matching code.
00184 */
00185 
00186 static int
00187 repute_name_to_code(struct repute_lookup *tbl, const char *name)
00188 {
00189        int c;
00190 
00191        assert(tbl != NULL);
00192        assert(name != NULL);
00193 
00194        for (c = 0; ; c++)
00195        {
00196               if (tbl[c].rt_name == NULL ||
00197                   strcasecmp(name, tbl[c].rt_name) == 0)
00198                      return tbl[c].rt_code;
00199        }
00200 
00201        return -1;
00202 }
00203 
00204 /*
00205 **  REPUTE_PARSE -- parse a REPUTE message
00206 **
00207 **  Parameters:
00208 **     buf -- buffer containing a REPUTE reply
00209 **     rep -- returned reputation
00210 **     conf -- confidence
00211 **     sample -- sample size
00212 **     limit -- recommented flow limit
00213 **     when -- timestamp on the report
00214 **
00215 **  Return value:
00216 **     A REPUTE_STAT_* constant.
00217 */
00218 
00219 static REPUTE_STAT
00220 repute_parse(const char *buf, size_t buflen, float *rep, float *conf,
00221              unsigned long *sample, unsigned long *limit, time_t *when)
00222 {
00223        _Bool found_dkim = FALSE;
00224        _Bool found_spam = FALSE;
00225        int code;
00226        float conftmp;
00227        float reptmp;
00228        unsigned long sampletmp;
00229        unsigned long limittmp;
00230        time_t whentmp;
00231        char *p;
00232        const char *start;
00233 #ifdef USE_XML2
00234        xmlDocPtr doc = NULL;
00235        xmlNode *node = NULL;
00236        xmlNode *reputon = NULL;
00237 #endif /* USE_XML2 */
00238 #ifdef USE_JANSSON
00239        json_t *root = NULL;
00240        json_t *exts = NULL;
00241        json_t *obj = NULL;
00242        json_error_t error;
00243 #endif /* USE_JANSSON */
00244 
00245        assert(buf != NULL);
00246        assert(rep != NULL);
00247 
00248 #ifdef USE_XML2
00249        xmlSetGenericErrorFunc(NULL, repute_libxml2_errhandler);
00250 #endif /* USE_XML2 */
00251 
00252        /* skip any header found */
00253        /* XXX -- this should verify a desirable Content-Type */
00254        for (start = buf; *start != '\0'; start++)
00255        {
00256               if (*start == '\n' && *(start + 1) == '\n')
00257               {
00258                      buflen = buflen - (start - buf + 2);
00259                      buf = start + 2;
00260                      break;
00261               }
00262               else if (*start == '\r' &&
00263                        *(start + 1) == '\n' &&
00264                        *(start + 2) == '\r' &&
00265                        *(start + 3) == '\n')
00266               {
00267                      buflen = buflen - (start - buf + 4);
00268                      buf = start + 4;
00269                      break;
00270               }
00271        }
00272 
00273 #ifdef USE_JANSSON
00274        root = json_loads(buf, 0, &error);
00275        if (root == NULL)
00276               return REPUTE_STAT_PARSE;
00277 
00278        exts = json_object_get(root, REPUTE_XML_EXTENSION);
00279        if (exts != NULL && !json_is_object(exts))
00280        {
00281               json_decref(root);
00282               return REPUTE_STAT_PARSE;
00283        }
00284 
00285        obj = json_object_get(root, REPUTE_XML_ASSERTION);
00286        if (obj != NULL && json_is_string(obj) &&
00287            strcasecmp(json_string_value(obj), REPUTE_ASSERT_SPAM) == 0)
00288               found_spam = TRUE;
00289 
00290        obj = json_object_get(exts, REPUTE_EXT_ID);
00291        if (obj != NULL && json_is_string(obj) &&
00292            strcasecmp(json_string_value(obj), REPUTE_EXT_ID_DKIM) == 0)
00293               found_dkim = TRUE;
00294 
00295        obj = json_object_get(exts, REPUTE_EXT_RATE);
00296        if (obj != NULL && json_is_number(obj))
00297               limittmp = (unsigned long) json_integer_value(obj);
00298 
00299        obj = json_object_get(root, REPUTE_XML_RATER_AUTH);
00300        if (obj != NULL && json_is_number(obj))
00301               conftmp = (float) json_real_value(obj);
00302 
00303        obj = json_object_get(root, REPUTE_XML_RATING);
00304        if (obj != NULL && json_is_number(obj))
00305               reptmp = (float) json_real_value(obj);
00306 
00307        obj = json_object_get(root, REPUTE_XML_SAMPLE_SIZE);
00308        if (obj != NULL && json_is_number(obj))
00309               sampletmp = (unsigned long) json_integer_value(obj);
00310 
00311        obj = json_object_get(root, REPUTE_XML_UPDATED);
00312        if (obj != NULL && json_is_number(obj))
00313               whentmp = (time_t) json_integer_value(obj);
00314 
00315        if (found_dkim && found_spam)
00316        {
00317               *rep = reptmp;
00318               if (conf != NULL)
00319                      *conf = conftmp;
00320               if (sample != NULL)
00321                      *sample = sampletmp;
00322               if (when != NULL)
00323                      *when = whentmp;
00324               if (limit != NULL)
00325                      *limit = limittmp;
00326        }
00327 #endif /* USE_JANSSON */
00328 
00329 #ifdef USE_XML2
00330        doc = xmlParseMemory(buf, buflen);
00331        if (doc == NULL)
00332               return REPUTE_STAT_PARSE;
00333 
00334        node = xmlDocGetRootElement(doc);
00335        if (node == NULL)
00336        {
00337               xmlFreeDoc(doc);
00338               return REPUTE_STAT_PARSE;
00339        }
00340 
00341        /* confirm root's name */
00342        if (node->name == NULL ||
00343            strcasecmp(node->name, REPUTE_NAME_REPUTATION) != 0 ||
00344            node->children == NULL)
00345        {
00346               xmlFreeDoc(doc);
00347               return REPUTE_STAT_PARSE;
00348        }
00349 
00350        /* iterate through nodes looking for a reputon */
00351        for (node = node->children; node != NULL; node = node->next)
00352        {
00353               /* skip unnamed things or things that aren't reputons */
00354               if (node->name == NULL ||
00355                   node->type != XML_ELEMENT_NODE ||
00356                   strcasecmp(node->name, REPUTE_NAME_REPUTON) != 0 ||
00357                   node->children == NULL)
00358                      continue;
00359 
00360               found_dkim = FALSE;
00361               found_spam = FALSE;
00362               conftmp = 0.;
00363               reptmp = 0.;
00364               sampletmp = 0L;
00365               limittmp = ULONG_MAX;
00366               whentmp = 0;
00367 
00368               for (reputon = node->children;
00369                    reputon != NULL;
00370                    reputon = reputon->next)
00371               {
00372                      /* look for the reputon */
00373                      if (reputon->name == NULL ||
00374                          reputon->type != XML_ELEMENT_NODE ||
00375                          reputon->children == NULL ||
00376                          reputon->children->content == NULL)
00377                             continue;
00378 
00379                      /* skip unknown names */
00380                      code = repute_name_to_code(repute_lookup_elements,
00381                                                 reputon->name);
00382                      if (code == -1)
00383                             continue;
00384 
00385                      switch (code)
00386                      {
00387                        case REPUTE_XML_CODE_RATER:
00388                             /*
00389                             **  We assume for now that we got an answer
00390                             **  from the same place we asked.
00391                             */
00392 
00393                             break;
00394 
00395                        case REPUTE_XML_CODE_RATER_AUTH:
00396                             conftmp = strtof(reputon->children->content,
00397                                              &p);
00398                             if (*p != '\0' || conftmp < 0 || conftmp > 1)
00399                                    continue;
00400 
00401                        case REPUTE_XML_CODE_ASSERTION:
00402                             if (strcasecmp(reputon->children->content,
00403                                            REPUTE_ASSERT_SPAM) == 0)
00404                                    found_spam = TRUE;
00405                             break;
00406 
00407                        case REPUTE_XML_CODE_EXTENSION:
00408                             if (strcasecmp(reputon->children->content,
00409                                            REPUTE_EXT_ID_BOTH) == 0)
00410                             {
00411                                    found_dkim = TRUE;
00412                             }
00413                             else if (strncasecmp(reputon->children->content,
00414                                                  REPUTE_EXT_RATE_COLON,
00415                                                  sizeof REPUTE_EXT_RATE_COLON - 1) == 0)
00416                             {
00417                                    errno = 0;
00418                                    limittmp = strtoul(reputon->children->content + sizeof REPUTE_EXT_RATE_COLON,
00419                                                        &p, 10);
00420                                    if (errno != 0)
00421                                           continue;
00422                             }
00423                             break;
00424 
00425                        case REPUTE_XML_CODE_RATED:
00426                             /*
00427                             **  We assume for now that we got an answer
00428                             **  to the right question.
00429                             */
00430 
00431                             break;
00432 
00433                        case REPUTE_XML_CODE_RATING:
00434                             reptmp = strtof(reputon->children->content,
00435                                             &p);
00436                             if (*p != '\0' || reptmp < -1 || reptmp > 1)
00437                                    continue;
00438                             break;
00439 
00440                        case REPUTE_XML_CODE_SAMPLE_SIZE:
00441                             errno = 0;
00442                             sampletmp = strtoul(reputon->children->content,
00443                                                 &p, 10);
00444                             if (errno != 0)
00445                                    continue;
00446                             break;
00447 
00448                        case REPUTE_XML_CODE_UPDATED:
00449                             errno = 0;
00450                             whentmp = strtoul(reputon->children->content,
00451                                               &p, 10);
00452                             if (errno != 0)
00453                                    continue;
00454                             break;
00455 
00456                        default:
00457                             break;
00458                      }
00459               }
00460 
00461               if (found_dkim && found_spam)
00462               {
00463                      *rep = reptmp;
00464                      if (conf != NULL)
00465                             *conf = conftmp;
00466                      if (sample != NULL)
00467                             *sample = sampletmp;
00468                      if (when != NULL)
00469                             *when = whentmp;
00470                      if (limit != NULL)
00471                             *limit = limittmp;
00472 
00473                      break;
00474               }
00475        }
00476 
00477        xmlFreeDoc(doc);
00478 #endif /* USE_XML2 */
00479 
00480        return REPUTE_STAT_OK;
00481 }
00482 
00483 /*
00484 **  REPUTE_GET_IO -- get or create an I/O handle
00485 **
00486 **  Parameters:
00487 **     rep -- REPUTE handle
00488 **
00489 **  Return value:
00490 **     An I/O handle if one could be either recycled or created, or NULL
00491 **     on failure.
00492 */
00493 
00494 static struct repute_io *
00495 repute_get_io(REPUTE rep)
00496 {
00497        assert(rep != NULL);
00498 
00499        struct repute_io *rio = NULL;
00500 
00501        pthread_mutex_lock(&rep->rep_lock);
00502 
00503        if (rep->rep_ios != NULL)
00504        {
00505               rio = rep->rep_ios;
00506 
00507               rep->rep_ios = rep->rep_ios->repute_next;
00508 
00509               rio->repute_offset = 0;
00510        }
00511        else
00512        {
00513               rio = malloc(sizeof *rio);
00514               if (rio != NULL)
00515               {
00516                      rio->repute_alloc = 0;
00517                      rio->repute_offset = 0;
00518                      rio->repute_buf = NULL;
00519                      rio->repute_next = NULL;
00520 
00521                      rio->repute_curl = curl_easy_init();
00522                      if (rio->repute_curl == NULL)
00523                      {
00524                             free(rio);
00525                             rio = NULL;
00526                      }
00527                      else
00528                      {
00529                             int status;
00530 
00531                             status = curl_easy_setopt(rio->repute_curl,
00532                                                       CURLOPT_WRITEFUNCTION,
00533                                                         repute_curl_writedata);
00534                             if (status != CURLE_OK)
00535                             {
00536                                    free(rio);
00537                                    rio = NULL;
00538                             }
00539 
00540                             if (rep->rep_useragent != NULL)
00541                             {
00542                                    (void) curl_easy_setopt(rio->repute_curl,
00543                                                            CURLOPT_USERAGENT,
00544                                                            rep->rep_useragent);
00545                             }
00546                      }
00547               }
00548        }
00549 
00550        pthread_mutex_unlock(&rep->rep_lock);
00551 
00552        return rio;
00553 }
00554 
00555 /*
00556 **  REPUTE_PUT_IO -- recycle an I/O handle
00557 **
00558 **  Parameters:
00559 **     rep -- REPUTE handle
00560 **     rio -- REPUTE I/O handle to be recycled
00561 **
00562 **  Return value:
00563 **     None.
00564 */
00565 
00566 static void
00567 repute_put_io(REPUTE rep, struct repute_io *rio)
00568 {
00569        assert(rep != NULL);
00570        assert(rio != NULL);
00571 
00572        pthread_mutex_lock(&rep->rep_lock);
00573 
00574        rio->repute_next = rep->rep_ios;
00575        rep->rep_ios = rio;
00576 
00577        pthread_mutex_unlock(&rep->rep_lock);
00578 }
00579 
00580 /*
00581 **  REPUTE_DOQUERY -- execute a query
00582 **
00583 **  Parameters:
00584 **
00585 **  Return value:
00586 **     A REPUTE_STAT_* constant.
00587 */
00588 
00589 static REPUTE_STAT
00590 repute_doquery(struct repute_io *rio, const char *url)
00591 {
00592        CURLcode cstatus;
00593        long rcode;
00594 
00595        assert(rio != NULL);
00596        assert(url != NULL);
00597 
00598        cstatus = curl_easy_setopt(rio->repute_curl, CURLOPT_WRITEDATA, rio);
00599        if (cstatus != CURLE_OK)
00600        {
00601               rio->repute_errcode = cstatus;
00602               return REPUTE_STAT_INTERNAL;
00603        }
00604 
00605        cstatus = curl_easy_setopt(rio->repute_curl, CURLOPT_URL, url);
00606        if (cstatus != CURLE_OK)
00607        {
00608               rio->repute_errcode = cstatus;
00609               return REPUTE_STAT_INTERNAL;
00610        }
00611 
00612        rio->repute_errcode = 0;
00613        rio->repute_rcode = 0;
00614        memset(rio->repute_buf, '\0', rio->repute_alloc);
00615 
00616        cstatus = curl_easy_perform(rio->repute_curl);
00617        if (cstatus != CURLE_OK)
00618        {
00619               rio->repute_errcode = cstatus;
00620               return REPUTE_STAT_QUERY;
00621        }
00622 
00623        cstatus = curl_easy_getinfo(rio->repute_curl, CURLINFO_RESPONSE_CODE,
00624                                    &rcode);
00625        if (rcode != 200)
00626               return REPUTE_STAT_QUERY;
00627 
00628        return REPUTE_STAT_OK;
00629 }
00630 
00631 /*
00632 **  REPUTE_GET_ERROR -- retrieve an error string
00633 **
00634 **  Parameters:
00635 **     rio -- repute I/O handle where an error occurred
00636 **     buf -- buffer to which to write the error string
00637 **     buflen -- bytes available at "buf"
00638 **
00639 **  Return value:
00640 **     None.
00641 */
00642 
00643 static void
00644 repute_get_error(struct repute_io *rio, char *buf, size_t buflen)
00645 {
00646        assert(rio != NULL);
00647        assert(buf != NULL);
00648 
00649        if (rio->repute_rcode != 0)
00650               snprintf(buf, buflen, "HTTP error code %u", rio->repute_rcode);
00651        else
00652               snprintf(buf, buflen, curl_easy_strerror(rio->repute_errcode));
00653 }
00654 
00655 /*
00656 **  REPUTE_GET_TEMPLATE -- retrieve a URI template for a service
00657 **
00658 **  Parameters:
00659 **     rep -- REPUTE handle
00660 **     buf -- buffer into which to write the retrieved template
00661 **     buflen -- bytes available at "buf"
00662 **
00663 **  Return value:
00664 **     A REPUTE_STAT_* constant.
00665 */
00666 
00667 static int
00668 repute_get_template(REPUTE rep)
00669 {
00670        int out;
00671        int cstatus;
00672        long rcode;
00673        struct repute_io *rio;
00674        URITEMP ut;
00675        char url[REPUTE_BUFBASE + 1];
00676 
00677        assert(rep != NULL);
00678 
00679        ut = ut_init();
00680        if (ut == NULL)
00681               return REPUTE_STAT_INTERNAL;
00682 
00683        if (ut_keyvalue(ut, UT_KEYTYPE_STRING,
00684                        "scheme", REPUTE_URI_SCHEME) != 0 ||
00685            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00686                        "service", (void *) rep->rep_server) != 0 ||
00687            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00688                        "application", REPUTE_URI_APPLICATION) != 0)
00689        {
00690               ut_destroy(ut);
00691               return REPUTE_STAT_INTERNAL;
00692        }
00693 
00694        if (ut_generate(ut, REPUTE_URI_TEMPLATE, url, sizeof url) <= 0)
00695        {
00696               ut_destroy(ut);
00697               return REPUTE_STAT_INTERNAL;
00698        }
00699 
00700        ut_destroy(ut);
00701 
00702        rio = repute_get_io(rep);
00703        if (rio == NULL)
00704               return REPUTE_STAT_INTERNAL;
00705 
00706        cstatus = curl_easy_setopt(rio->repute_curl, CURLOPT_WRITEDATA, rio);
00707        if (cstatus != CURLE_OK)
00708        {
00709               snprintf(rep->rep_error, sizeof rep->rep_error, "%s",
00710                        curl_easy_strerror(cstatus));
00711               repute_put_io(rep, rio);
00712               return REPUTE_STAT_INTERNAL;
00713        }
00714 
00715        cstatus = curl_easy_setopt(rio->repute_curl, CURLOPT_URL, url);
00716        if (cstatus != CURLE_OK)
00717        {
00718               snprintf(rep->rep_error, sizeof rep->rep_error, "%s",
00719                        curl_easy_strerror(cstatus));
00720               repute_put_io(rep, rio);
00721               return REPUTE_STAT_INTERNAL;
00722        }
00723 
00724        cstatus = curl_easy_perform(rio->repute_curl);
00725        if (cstatus != CURLE_OK)
00726        {
00727               snprintf(rep->rep_error, sizeof rep->rep_error, "%s",
00728                        curl_easy_strerror(cstatus));
00729               repute_put_io(rep, rio);
00730               return REPUTE_STAT_QUERY;
00731        }
00732 
00733        cstatus = curl_easy_getinfo(rio->repute_curl, CURLINFO_RESPONSE_CODE,
00734                                    &rcode);
00735        if (rcode != 200)
00736        {
00737               snprintf(rep->rep_error, sizeof rep->rep_error,
00738                        "HTTP response code %u", (unsigned int) rcode);
00739               repute_put_io(rep, rio);
00740               return REPUTE_STAT_QUERY;
00741        }
00742 
00743        (void) snprintf(rep->rep_uritemp, sizeof rep->rep_uritemp, "%s",
00744                        rio->repute_buf);
00745        if (rep->rep_uritemp[rio->repute_offset - 1] == '\n')
00746               rep->rep_uritemp[rio->repute_offset - 1] = '\0';
00747 
00748        repute_put_io(rep, rio);
00749 
00750        return REPUTE_STAT_OK;
00751 }
00752 
00753 /*
00754 **  REPUTE_INIT -- initialize REPUTE subsystem
00755 **
00756 **  Parameters:
00757 **     None.
00758 **
00759 **  Return value:
00760 **     None.
00761 */
00762 
00763 void
00764 repute_init(void)
00765 {
00766 #ifdef USE_XML2
00767        xmlInitParser();
00768 #endif /* USE_XML2 */
00769 
00770        curl_global_init(CURL_GLOBAL_ALL);
00771 }
00772 
00773 /*
00774 **  REPUTE_NEW -- make a new REPUTE handle
00775 **
00776 **  Parameters:
00777 **     server -- server hostname
00778 **     reporter -- reporter ID to use
00779 **
00780 **  Return value:
00781 **     A new REPUTE handle on success, NULL on failure.
00782 */
00783 
00784 REPUTE
00785 repute_new(const char *server, unsigned int reporter)
00786 {
00787        struct repute_handle *new;
00788        curl_version_info_data *vinfo;
00789 
00790        assert(server != NULL);
00791 
00792        new = malloc(sizeof *new);
00793        if (new == NULL)
00794               return NULL;
00795 
00796        memset(new, '\0', sizeof *new);
00797 
00798        new->rep_reporter = reporter;
00799        new->rep_server = strdup(server);
00800        if (new->rep_server == NULL)
00801        {
00802               free(new);
00803               return NULL;
00804        }
00805 
00806        vinfo = curl_version_info(CURLVERSION_NOW);
00807        if (vinfo != NULL && vinfo->version != NULL)
00808               new->rep_curlversion = strdup(vinfo->version);
00809 
00810        pthread_mutex_init(&new->rep_lock, NULL);
00811 
00812        return new;
00813 }
00814 
00815 /*
00816 **  REPUTE_CLOSE -- tear down a REPUTE handle
00817 **
00818 **  Paramters:
00819 **     rep -- REPUTE handle to shut down
00820 **
00821 **  Return value:
00822 **     None.
00823 */
00824 
00825 void
00826 repute_close(REPUTE rep)
00827 {
00828        struct repute_io *rio;
00829        struct repute_io *next;
00830 
00831        assert(rep != NULL);
00832 
00833        rio = rep->rep_ios;
00834        while (rio != NULL)
00835        {
00836               next = rio->repute_next;
00837 
00838               if (rio->repute_buf != NULL)
00839                      free(rio->repute_buf);
00840               if (rio->repute_curl != NULL)
00841                      curl_easy_cleanup(rio->repute_curl);
00842               free(rio);
00843 
00844               rio = next;
00845        }
00846 
00847        pthread_mutex_destroy(&rep->rep_lock);
00848 
00849        free((void *) rep->rep_server);
00850 
00851        free(rep);
00852 }
00853 
00854 /*
00855 **  REPUTE_CURLVERSION -- get libcurl version string
00856 **
00857 **  Parameters:
00858 **     rep -- REPUTE handle
00859 **
00860 **  Return value:
00861 **     A pointer to a string containing the libcurl version, or NULL.
00862 */
00863 
00864 const char *
00865 repute_curlversion(REPUTE rep)
00866 {
00867        assert(rep != NULL);
00868 
00869        return rep->rep_curlversion;
00870 }
00871 
00872 /*
00873 **  REPUTE_USERAGENT -- set user agent for REPUTE queries
00874 **
00875 **  Parameters:
00876 **     rep -- REPUTE handle
00877 **     ua -- User-Agent string to use
00878 **
00879 **  Return value:
00880 **     None.
00881 */
00882 
00883 void
00884 repute_useragent(REPUTE rep, const char *ua)
00885 {
00886        if (rep->rep_useragent != NULL)
00887               free((void *) rep->rep_useragent);
00888 
00889        rep->rep_useragent = strdup(ua);
00890 }
00891 
00892 /*
00893 **  REPUTE_QUERY -- query a REPUTE server for a spam reputation
00894 **
00895 **  Parameters:
00896 **     rep -- REPUTE handle
00897 **     domain -- domain of interest
00898 **     repout -- reputation (returned)
00899 **     confout -- confidence (returned)
00900 **     sampout -- sample count (returned)
00901 **     limitout -- limit (returned)
00902 **     whenout -- update timestamp (returned)
00903 **
00904 **  Return value:
00905 **     A REPUTE_STAT_* constant.
00906 */
00907 
00908 REPUTE_STAT
00909 repute_query(REPUTE rep, const char *domain, float *repout,
00910              float *confout, unsigned long *sampout, unsigned long *limitout,
00911              time_t *whenout)
00912 {
00913        REPUTE_STAT status;
00914        float conf;
00915        float reputation;
00916        unsigned long samples;
00917        unsigned long limit;
00918        time_t when;
00919        struct repute_io *rio;
00920        URITEMP ut;
00921        char genurl[REPUTE_URL];
00922        char template[REPUTE_URL];
00923 
00924        assert(rep != NULL);
00925        assert(domain != NULL);
00926        assert(repout != NULL);
00927 
00928        if (rep->rep_uritemp[0] == '\0')
00929        {
00930               if (repute_get_template(rep) != REPUTE_STAT_OK)
00931                      return REPUTE_STAT_QUERY;
00932        }
00933 
00934        ut = ut_init();
00935        if (ut == NULL)
00936               return REPUTE_STAT_INTERNAL;
00937 
00938        if (rep->rep_reporter != 0)
00939        {
00940               snprintf(genurl, sizeof genurl, "%u", rep->rep_reporter);
00941               if (ut_keyvalue(ut, UT_KEYTYPE_STRING,
00942                               "reporter", genurl) != 0)
00943               {
00944                      ut_destroy(ut);
00945                      return REPUTE_STAT_INTERNAL;
00946               }
00947        }
00948 
00949        if (ut_keyvalue(ut, UT_KEYTYPE_STRING,
00950                        "subject", (void *) domain) != 0 ||
00951 #ifdef USE_JANSSON
00952            ut_keyvalue(ut, UT_KEYTYPE_STRING, "format", "json") != 0 ||
00953 #endif /* USE_JANSSON */
00954 #ifdef USE_XML2
00955            ut_keyvalue(ut, UT_KEYTYPE_STRING, "format", "xml") != 0 ||
00956 #endif /* USE_XML2 */
00957            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00958                        "scheme", REPUTE_URI_SCHEME) != 0 ||
00959            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00960                        "service", (void *) rep->rep_server) != 0 ||
00961            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00962                        "application", REPUTE_URI_APPLICATION) != 0 ||
00963            ut_keyvalue(ut, UT_KEYTYPE_STRING,
00964                        "assertion", REPUTE_ASSERT_SPAM) != 0)
00965        {
00966               ut_destroy(ut);
00967               return REPUTE_STAT_INTERNAL;
00968        }
00969 
00970        if (ut_generate(ut, rep->rep_uritemp, genurl, sizeof genurl) <= 0)
00971        {
00972               ut_destroy(ut);
00973               return REPUTE_STAT_INTERNAL;
00974        }
00975 
00976        ut_destroy(ut);
00977 
00978        rio = repute_get_io(rep);
00979        if (rio == NULL)
00980               return REPUTE_STAT_INTERNAL;
00981 
00982        status = repute_doquery(rio, genurl);
00983        if (status != REPUTE_STAT_OK)
00984        {
00985               repute_get_error(rio, rep->rep_error, sizeof rep->rep_error);
00986               repute_put_io(rep, rio);
00987               return status;
00988        }
00989 
00990        status = repute_parse(rio->repute_buf, rio->repute_offset,
00991                              &reputation, &conf, &samples, &limit, &when);
00992        if (status != REPUTE_STAT_OK)
00993        {
00994               snprintf(rep->rep_error, sizeof rep->rep_error,
00995                        "error parsing reply");
00996               repute_put_io(rep, rio);
00997               return status;
00998        }
00999 
01000        *repout = reputation;
01001        if (confout != NULL)
01002               *confout = conf;
01003        if (sampout != NULL)
01004               *sampout = samples;
01005        if (whenout != NULL)
01006               *whenout = when;
01007        if (limitout != NULL)
01008               *limitout = limit;
01009 
01010        repute_put_io(rep, rio);
01011 
01012        return REPUTE_STAT_OK;
01013 }
01014 
01015 /*
01016 **  REPUTE_ERROR -- return a pointer to the error buffer
01017 **
01018 **  Parameters:
01019 **     rep -- REPUTE handle
01020 **
01021 **  Return value:
01022 **     Pointer to the error buffer inside the REPUTE handle.
01023 */
01024 
01025 const char *
01026 repute_error(REPUTE rep)
01027 {
01028        assert(rep != NULL);
01029 
01030        return rep->rep_error;
01031 }