LCOV - code coverage report
Current view: top level - cmail-smtpd - path.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 72 95 75.8 %
Date: 2015-11-25 19:06:20 Functions: 3 3 100.0 %

          Line data    Source code
       1             : //TODO test this thoroughly
       2          85 : int path_parse(LOGGER log, char* pathspec, MAILPATH* path){
       3             :         //See http://cr.yp.to/smtp/address.html for hints on address parsing
       4          85 :         bool quotes = false, done_parsing = false, comment = false;
       5          85 :         unsigned out_pos = 0, in_pos = 0;
       6             : 
       7             :         //skip leading spaces
       8          85 :         for(; isspace(pathspec[0]); pathspec++){
       9             :         }
      10             : 
      11          85 :         logprintf(log, LOG_DEBUG, "Parsing path %s\n", pathspec);
      12             : 
      13        1389 :         for(in_pos = 0; !done_parsing && out_pos < (SMTP_MAX_PATH_LENGTH - 1) && pathspec[in_pos]; in_pos++){
      14        1306 :                 if(!comment){
      15        1245 :                         switch(pathspec[in_pos]){
      16             :                                 case '@':
      17          70 :                                         if(out_pos == 0){
      18             :                                                 //route syntax. skip until next colon.
      19           0 :                                                 for(; pathspec[in_pos] && pathspec[in_pos] != ':'; in_pos++){
      20             :                                                 }
      21           0 :                                                 if(pathspec[in_pos] != ':'){
      22             :                                                         //colon was somehow the last character. someone blew this.
      23           0 :                                                         done_parsing = true;
      24             :                                                 }
      25             :                                         }
      26             :                                         else{
      27          70 :                                                 if(quotes){
      28           1 :                                                         path->path[out_pos++] = pathspec[in_pos];
      29             :                                                 }
      30          69 :                                                 else if(path->delimiter_position == 0 && path->path[path->delimiter_position] != '@'){
      31             :                                                         //copy to out buffer and update delimiter position
      32          68 :                                                         path->delimiter_position = out_pos;
      33          68 :                                                         path->path[out_pos++] = pathspec[in_pos];
      34             :                                                 }
      35             :                                                 else{
      36           1 :                                                         logprintf(log, LOG_WARNING, "Multiple delimiters in path\n");
      37           1 :                                                         return -1;
      38             :                                                 }
      39             :                                         }
      40          69 :                                         break;
      41             :                                 case '"':
      42           6 :                                         quotes = !quotes;
      43           6 :                                         break;
      44             :                                 case '\\':
      45             :                                         //escape character. add next char to out buffer
      46             :                                         //WARNING this can cause an issue when implemented incorrectly.
      47             :                                         //if the last character sent is a backslash escaping \0 the
      48             :                                         //loop potentially accesses memory out of bounds.
      49             :                                         //so, we check for that.
      50             :                                         //actually, in this implementation, there are at least
      51             :                                         //2 \0 bytes in that case, so this is a non-issue.
      52             :                                         //FIXME allow only printable/space characters here
      53           0 :                                         if(pathspec[in_pos + 1]){
      54           0 :                                                 in_pos++;
      55           0 :                                                 if(isprint(pathspec[in_pos])){
      56           0 :                                                         path->path[out_pos++] = pathspec[in_pos];
      57             :                                                 }
      58             :                                         }
      59             :                                         else{
      60           0 :                                                 done_parsing = true;
      61           0 :                                                 break;
      62             :                                         }
      63           0 :                                         break;
      64             :                                 case '(':
      65             :                                         //comment delimiter
      66           3 :                                         comment = true;
      67           3 :                                         break;
      68             :                                 case ')':
      69             :                                         //comment closed without active comment context
      70           1 :                                         logprintf(log, LOG_WARNING, "Path contained illegal parenthesis\n");
      71           1 :                                         return -1;
      72             :                                 case '<':
      73          85 :                                         if(!quotes){
      74             :                                                 //start mark. ignore it.
      75          84 :                                                 break;
      76             :                                         }
      77             :                                         //fall through
      78             :                                 case '>':
      79             :                                         //FIXME allow only printable nonspace(?) characters here
      80          83 :                                         if(!quotes){
      81          81 :                                                 done_parsing = true;
      82          81 :                                                 break;
      83             :                                         }
      84             :                                         //fall through
      85             :                                 default:
      86             :                                         //copy to out buffer
      87        1000 :                                         if(isprint(pathspec[in_pos])){
      88        1000 :                                                 path->path[out_pos++] = pathspec[in_pos];
      89             :                                         }
      90             :                         }
      91             :                 }
      92          61 :                 else if(pathspec[in_pos] == ')'){
      93           2 :                         comment = false;
      94             :                 }
      95             :         }
      96             : 
      97          83 :         path->path[out_pos] = 0;
      98             : 
      99          83 :         if(comment){
     100           1 :                 logprintf(log, LOG_WARNING, "Path contains unterminated comment\n");
     101           1 :                 return -1;
     102             :         }
     103             : 
     104          82 :         if(!path->delimiter_position){
     105          15 :                 path->delimiter_position = strlen(path->path);
     106             :         }
     107             : 
     108          82 :         logprintf(log, LOG_DEBUG, "Result is %s, delimiter is at %d\n", path->path, path->delimiter_position);
     109          82 :         return 0;
     110             : }
     111             : 
     112             : // If originating_user is set, this checks if the user may use the path outbound
     113          82 : int path_resolve(LOGGER log, MAILPATH* path, DATABASE* database, char* originating_user, bool is_reverse){
     114          82 :         int status, rv = -1;
     115             : 
     116             :         //this early exit should never have to be taken
     117          82 :         if(path->route.router){
     118           0 :                 logprintf(log, LOG_WARNING, "Taking early exit for path %s, please notify the developers\n", path->path);
     119           0 :                 return 0;
     120             :         }
     121             : 
     122          82 :         status = sqlite3_bind_text(database->query_address, 1, path->path, -1, SQLITE_STATIC);
     123          82 :         if(status == SQLITE_OK){
     124             :                 do{
     125          82 :                         status = sqlite3_step(database->query_address);
     126          82 :                         if(status == SQLITE_ROW){
     127          45 :                                 if(originating_user || is_reverse){
     128             :                                         //Test whether the originating_user may user the supplied path outbound.
     129             :                                         //This implies traversing all entries and testing the following conditions
     130             :                                         //      1. Router must be 'store'
     131             :                                         //      2. Route must be the originating user
     132             :                                         //      Else, try the next entry
     133           1 :                                         if(originating_user && 
     134           0 :                                                 (strcmp((char*)sqlite3_column_text(database->query_address, 0), "store") 
     135           0 :                                                         || strcmp(originating_user, (char*)sqlite3_column_text(database->query_address, 1)))){
     136             :                                                 // Falling through to SQLITE_DONE here implies a non-local origin while routers for this adress are set
     137             :                                                 // (just not for this user). The defined router will then fail the address, the any router will accept it
     138           0 :                                                 continue;
     139             :                                         }
     140             : 
     141             :                                         //Continuing here if no originating_user given or the current routing information
     142             :                                         //applies to the originating_user
     143             :                                 }
     144             : 
     145             :                                 //heap-copy the routing information
     146          45 :                                 path->route.router = common_strdup((char*)sqlite3_column_text(database->query_address, 0));
     147          45 :                                 if(sqlite3_column_text(database->query_address, 1)){
     148          22 :                                         path->route.argument = common_strdup((char*)sqlite3_column_text(database->query_address, 1));
     149             :                                 }
     150             : 
     151          45 :                                 if(!path->route.router){
     152           0 :                                         logprintf(log, LOG_ERROR, "Failed to allocate storage for routing data\n");
     153             :                                         //fail temporarily
     154           0 :                                         rv = -1;
     155           0 :                                         break;
     156             :                                 }
     157             : 
     158             :                                 //check for reject
     159          45 :                                 if(!strcmp(path->route.router, "reject")){
     160           5 :                                         rv = 1;
     161           5 :                                         break;
     162             :                                 }
     163             : 
     164             :                                 //all is well
     165          40 :                                 rv = 0;
     166          40 :                                 break;
     167             :                         }
     168          37 :                 } while(status == SQLITE_ROW);
     169             : 
     170          82 :                 switch(status){
     171             :                         case SQLITE_ROW:
     172             :                                 //already handled during loop
     173          45 :                                 break;
     174             :                         case SQLITE_DONE:
     175          37 :                                 logprintf(log, LOG_INFO, "No address match found\n");
     176             :                                 //continue with this path marked as non-local
     177          37 :                                 rv = 0;
     178          37 :                                 break;
     179             :                         default:
     180           0 :                                 logprintf(log, LOG_ERROR, "Failed to query wildcard: %s\n", sqlite3_errmsg(database->conn));
     181           0 :                                 rv = -1;
     182           0 :                                 break;
     183             :                 }
     184             :         }
     185             :         else{
     186           0 :                 logprintf(log, LOG_ERROR, "Failed to bind search parameter: %s\n", sqlite3_errmsg(database->conn));
     187           0 :                 rv = -1;
     188             :         }
     189             : 
     190          82 :         sqlite3_reset(database->query_address);
     191          82 :         sqlite3_clear_bindings(database->query_address);
     192             : 
     193             :         // Calling contract
     194             :         //      0 -> Accept path
     195             :         //      1 -> Reject path (500), if possible use router argument
     196             :         //      * -> Reject path (400)
     197          82 :         return rv;
     198             : }
     199             : 
     200         148 : void path_reset(MAILPATH* path){
     201         148 :         MAILPATH reset_path = {
     202             :                 .delimiter_position = 0,
     203             :                 .in_transaction = false,
     204             :                 .path = "",
     205             :                 .route = {
     206             :                         .router = NULL,
     207             :                         .argument = NULL
     208             :                 }
     209             :         };
     210             : 
     211         148 :         route_free(&(path->route));
     212             : 
     213         148 :         *path = reset_path;
     214         148 : }

Generated by: LCOV version 1.11