"Fossies" - the Fresh Open Source Software Archive

Member "ncdc-1.22.1/src/proto.c" (26 Mar 2019, 10898 Bytes) of package /linux/privat/ncdc-1.22.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "proto.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.20_vs_1.21.

    1 /* ncdc - NCurses Direct Connect client
    2 
    3   Copyright (c) 2011-2019 Yoran Heling
    4 
    5   Permission is hereby granted, free of charge, to any person obtaining
    6   a copy of this software and associated documentation files (the
    7   "Software"), to deal in the Software without restriction, including
    8   without limitation the rights to use, copy, modify, merge, publish,
    9   distribute, sublicense, and/or sell copies of the Software, and to
   10   permit persons to whom the Software is furnished to do so, subject to
   11   the following conditions:
   12 
   13   The above copyright notice and this permission notice shall be included
   14   in all copies or substantial portions of the Software.
   15 
   16   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   17   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   18   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   19   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   20   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   21   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   22   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   23 
   24 */
   25 
   26 
   27 #include "ncdc.h"
   28 #include "proto.h"
   29 
   30 
   31 // NMDC support
   32 
   33 char *charset_convert(hub_t *hub, gboolean to_utf8, const char *str) {
   34   char *fmt = var_get(hub->id, VAR_encoding);
   35   char *res = str_convert(to_utf8?"UTF-8":fmt, !to_utf8?"UTF-8":fmt, str);
   36   return res;
   37 }
   38 
   39 
   40 char *nmdc_encode_and_escape(hub_t *hub, const char *str) {
   41   char *enc = charset_convert(hub, FALSE, str);
   42   GString *dest = g_string_sized_new(strlen(enc));
   43   char *tmp = enc;
   44   while(*tmp) {
   45     if(*tmp == '$')
   46       g_string_append(dest, "$");
   47     else if(*tmp == '|')
   48       g_string_append(dest, "|");
   49     else if(*tmp == '&' && (strncmp(tmp, "&", 5) == 0 || strncmp(tmp, "$", 5) == 0 || strncmp(tmp, "|", 6) == 0))
   50       g_string_append(dest, "&");
   51     else
   52       g_string_append_c(dest, *tmp);
   53     tmp++;
   54   }
   55   g_free(enc);
   56   return g_string_free(dest, FALSE);
   57 }
   58 
   59 
   60 char *nmdc_unescape_and_decode(hub_t *hub, const char *str) {
   61   GString *dest = g_string_sized_new(strlen(str));
   62   while(*str) {
   63     if(strncmp(str, "$", 5) == 0) {
   64       g_string_append_c(dest, '$');
   65       str += 5;
   66     } else if(strncmp(str, "|", 6) == 0) {
   67       g_string_append_c(dest, '|');
   68       str += 6;
   69     } else if(strncmp(str, "&", 5) == 0) {
   70       g_string_append_c(dest, '&');
   71       str += 5;
   72     } else {
   73       g_string_append_c(dest, *str);
   74       str++;
   75     }
   76   }
   77   char *dec = charset_convert(hub, TRUE, dest->str);
   78   g_string_free(dest, TRUE);
   79   return dec;
   80 }
   81 
   82 
   83 // Info & algorithm @ http://www.teamfair.info/wiki/index.php?title=Lock_to_key
   84 // This function modifies "lock" in-place for temporary data
   85 char *nmdc_lock2key(char *lock) {
   86   char n;
   87   int i;
   88   int len = strlen(lock);
   89   if(len < 3)
   90     return g_strdup("STUPIDKEY!"); // let's not crash on invalid data
   91   int fst = lock[0] ^ lock[len-1] ^ lock[len-2] ^ 5;
   92   for(i=len-1; i; i--)
   93     lock[i] = lock[i] ^ lock[i-1];
   94   lock[0] = fst;
   95   for(i=0; i<len; i++)
   96     lock[i] = ((lock[i]<<4) & 0xF0) | ((lock[i]>>4) & 0x0F);
   97   GString *key = g_string_sized_new(len+100);
   98   for(i=0; i<len; i++) {
   99     n = lock[i];
  100     if(n == 0 || n == 5 || n == 36 || n == 96 || n == 124 || n == 126)
  101       g_string_append_printf(key, "/%%DCN%03d%%/", n);
  102     else
  103       g_string_append_c(key, n);
  104   }
  105   return g_string_free(key, FALSE);
  106 }
  107 
  108 
  109 
  110 
  111 
  112 // ADC support
  113 
  114 
  115 // ADC parameter unescaping
  116 char *adc_unescape(const char *str, gboolean nmdc) {
  117   char *dest = g_new(char, strlen(str)+1);
  118   char *tmp = dest;
  119   while(*str) {
  120     if(*str == '\\') {
  121       str++;
  122       if(*str == 's' || (nmdc && *str == ' '))
  123         *tmp = ' ';
  124       else if(*str == 'n')
  125         *tmp = '\n';
  126       else if(*str == '\\')
  127         *tmp = '\\';
  128       else {
  129         g_free(dest);
  130         return NULL;
  131       }
  132     } else
  133       *tmp = *str;
  134     tmp++;
  135     str++;
  136   }
  137   *tmp = 0;
  138   return dest;
  139 }
  140 
  141 
  142 // ADC parameter escaping
  143 char *adc_escape(const char *str, gboolean nmdc) {
  144   GString *dest = g_string_sized_new(strlen(str)+50);
  145   while(*str) {
  146     switch(*str) {
  147     case ' ':  g_string_append(dest, nmdc ? "\\ " : "\\s"); break;
  148     case '\n': g_string_append(dest, "\\n"); break;
  149     case '\\': g_string_append(dest, "\\\\"); break;
  150     default: g_string_append_c(dest, *str); break;
  151     }
  152     str++;
  153   }
  154   return g_string_free(dest, FALSE);
  155 }
  156 
  157 
  158 #if INTERFACE
  159 
  160 // Only writes to the first 4 bytes of str, does not add a padding \0.
  161 #define ADC_EFCC(sid, str) do {\
  162     (str)[0] = ((sid)>> 0) & 0xFF;\
  163     (str)[1] = ((sid)>> 8) & 0xFF;\
  164     (str)[2] = ((sid)>>16) & 0xFF;\
  165     (str)[3] = ((sid)>>24) & 0xFF;\
  166   } while(0)
  167 
  168 // and the reverse
  169 #define ADC_DFCC(str) ((str)[0] + ((str)[1]<<8) + ((str)[2]<<16) + ((str)[3]<<24))
  170 
  171 
  172 #define ADC_TOCMDV(a, b, c) ((a) + ((b)<<8) + ((c)<<16))
  173 #define ADC_TOCMD(str) ADC_TOCMDV((str)[0], (str)[1], (str)[2])
  174 
  175 enum adc_cmd_type {
  176 #define C(n, a, b, c) ADCC_##n = ADC_TOCMDV(a,b,c)
  177   // Base commands (copied from DC++ / AdcCommand.h)
  178   C(SUP, 'S','U','P'), // F,T,C    - PROTOCOL, NORMAL
  179   C(STA, 'S','T','A'), // F,T,C,U  - All
  180   C(INF, 'I','N','F'), // F,T,C    - IDENTIFY, NORMAL
  181   C(MSG, 'M','S','G'), // F,T      - NORMAL
  182   C(SCH, 'S','C','H'), // F,T,C    - NORMAL (can be in U, but is discouraged)
  183   C(RES, 'R','E','S'), // F,T,C,U  - NORMAL
  184   C(CTM, 'C','T','M'), // F,T      - NORMAL
  185   C(RCM, 'R','C','M'), // F,T      - NORMAL
  186   C(GPA, 'G','P','A'), // F        - VERIFY
  187   C(PAS, 'P','A','S'), // T        - VERIFY
  188   C(QUI, 'Q','U','I'), // F        - IDENTIFY, VERIFY, NORMAL
  189   C(GET, 'G','E','T'), // C        - NORMAL (extensions may use in it F/T as well)
  190   C(GFI, 'G','F','I'), // C        - NORMAL
  191   C(SND, 'S','N','D'), // C        - NORMAL (extensions may use it in F/T as well)
  192   C(SID, 'S','I','D')  // F        - PROTOCOL
  193 #undef C
  194 };
  195 
  196 
  197 struct adc_cmd_t {
  198   char type;        // B|C|D|E|F|H|I|U
  199   adc_cmd_type cmd; // ADCC_*, but can also be something else. Unhandled commands should be ignored anyway.
  200   int source;       // Only when type = B|D|E|F
  201   int dest;         // Only when type = D|E
  202   char **argv;
  203   int argc;
  204 };
  205 
  206 
  207 // ADC Protocol states.
  208 #define ADC_S_PROTOCOL 0
  209 #define ADC_S_IDENTIFY 1
  210 #define ADC_S_VERIFY   2
  211 #define ADC_S_NORMAL   3
  212 #define ADC_S_DATA     4
  213 
  214 #endif
  215 
  216 
  217 static gboolean int_in_array(const int *arr, int needle) {
  218   for(; arr&&*arr; arr++)
  219     if(*arr == needle)
  220       return TRUE;
  221   return FALSE;
  222 }
  223 
  224 
  225 gboolean adc_parse(const char *str, adc_cmd_t *c, int *feats, GError **err) {
  226   if(!g_utf8_validate(str, -1, NULL)) {
  227     g_set_error_literal(err, 1, 0, "Invalid encoding.");
  228     return FALSE;
  229   }
  230 
  231   if(strlen(str) < 4) {
  232     g_set_error_literal(err, 1, 0, "Message too short.");
  233     return FALSE;
  234   }
  235 
  236   if(*str != 'B' && *str != 'C' && *str != 'D' && *str != 'E' && *str != 'F' && *str != 'H' && *str != 'I' && *str != 'U') {
  237     g_set_error_literal(err, 1, 0, "Invalid ADC type");
  238     return FALSE;
  239   }
  240   c->type = *str;
  241   c->cmd = ADC_TOCMD(str+1);
  242 
  243   const char *off = str+4;
  244   if(off[0] && off[0] != ' ') {
  245     g_set_error_literal(err, 1, 0, "Invalid characters after command.");
  246     return FALSE;
  247   }
  248   if(off[0])
  249     off++;
  250 
  251   // type = U, first argument is source CID. But we don't handle that here.
  252 
  253   // type = B|D|E|F, first argument must be the source SID
  254   if(c->type == 'B' || c->type == 'D' || c->type == 'E' || c->type == 'F') {
  255     if(strlen(off) < 4) {
  256       g_set_error_literal(err, 1, 0, "Message too short");
  257       return FALSE;
  258     }
  259     c->source = ADC_DFCC(off);
  260     if(off[4] && off[4] != ' ') {
  261       g_set_error_literal(err, 1, 0, "Invalid characters after argument.");
  262       return FALSE;
  263     }
  264     off += off[4] ? 5 : 4;
  265   }
  266 
  267   // type = D|E, next argument must be the destination SID
  268   if(c->type == 'D' || c->type == 'E') {
  269     if(strlen(off) < 4) {
  270       g_set_error_literal(err, 1, 0, "Message too short");
  271       return FALSE;
  272     }
  273     c->dest = ADC_DFCC(off);
  274     if(off[4] && off[4] != ' ') {
  275       g_set_error_literal(err, 1, 0, "Invalid characters after argument.");
  276       return FALSE;
  277     }
  278     off += off[4] ? 5 : 4;
  279   }
  280 
  281   // type = F, next argument must be the feature list. We'll match this with
  282   // the 'feats' list (space separated list of FOURCCs, to make things easier)
  283   // to make sure it's correct. Some hubs broadcast F messages without actually
  284   // checking the listed features. :-/
  285   if(c->type == 'F') {
  286     int l = strchr(off, ' ') ? strchr(off, ' ')-off : strlen(off);
  287     if((l % 5) != 0) {
  288       g_set_error_literal(err, 1, 0, "Message too short");
  289       return FALSE;
  290     }
  291     int i;
  292     for(i=0; i<l/5; i++) {
  293       int f = ADC_DFCC(off+i*5+1);
  294       if(off[i*5] == '+' && !int_in_array(feats, f)) {
  295         g_set_error_literal(err, 1, 0, "Feature broadcast for a feature we don't have.");
  296         return FALSE;
  297       }
  298       if(off[i*5] == '-' && int_in_array(feats, f)) {
  299         g_set_error_literal(err, 1, 0, "Feature broadcast excluding a feature we have.");
  300         return FALSE;
  301       }
  302     }
  303     off += off[l] ? l+1 : l;
  304   }
  305 
  306   // parse the rest of the arguments
  307   char **s = g_strsplit(off, " ", 0);
  308   c->argc = s ? g_strv_length(s) : 0;
  309   if(s) {
  310     char **a = g_new0(char *, c->argc+1);
  311     int i;
  312     for(i=0; i<c->argc; i++) {
  313       a[i] = adc_unescape(s[i], FALSE);
  314       if(!a[i]) {
  315         g_set_error_literal(err, 1, 0, "Invalid escape in argument.");
  316         break;
  317       }
  318     }
  319     g_strfreev(s);
  320     if(i < c->argc) {
  321       g_strfreev(a);
  322       return FALSE;
  323     }
  324     c->argv = a;
  325   } else
  326     c->argv = NULL;
  327 
  328   return TRUE;
  329 }
  330 
  331 
  332 char *adc_getparam(char **a, char *name, char ***left) {
  333   while(a && *a) {
  334     if(**a && **a == name[0] && (*a)[1] == name[1]) {
  335       if(left)
  336         *left = a+1;
  337       return *a+2;
  338     }
  339     a++;
  340   }
  341   return NULL;
  342 }
  343 
  344 
  345 // Get all parameters with the given name. Return value should be g_free()'d,
  346 // NOT g_strfreev()'ed.
  347 char **adc_getparams(char **a, char *name) {
  348   int n = 10;
  349   char **res = g_new(char *, n);
  350   int i = 0;
  351   while(a && *a) {
  352     if(**a && **a == name[0] && (*a)[1] == name[1])
  353       res[i++] = *a+2;
  354     if(i >= n) {
  355       n += 10;
  356       res = g_realloc(res, n*sizeof(char *));
  357     }
  358     a++;
  359   }
  360   res[i] = NULL;
  361   if(res[0])
  362     return res;
  363   g_free(res);
  364   return NULL;
  365 }
  366 
  367 
  368 GString *adc_generate(char type, int cmd, int source, int dest) {
  369   GString *c = g_string_sized_new(100);
  370   g_string_append_c(c, type);
  371   char r[5] = {};
  372   ADC_EFCC(cmd, r);
  373   g_string_append(c, r);
  374 
  375   if(source) {
  376     g_string_append_c(c, ' ');
  377     ADC_EFCC(source, r);
  378     g_string_append(c, r);
  379   }
  380 
  381   if(dest) {
  382     g_string_append_c(c, ' ');
  383     ADC_EFCC(dest, r);
  384     g_string_append(c, r);
  385   }
  386 
  387   return c;
  388 }
  389 
  390 
  391 void adc_append(GString *c, const char *name, const char *arg) {
  392   g_string_append_c(c, ' ');
  393   if(name)
  394     g_string_append(c, name);
  395   char *enc = adc_escape(arg, FALSE);
  396   g_string_append(c, enc);
  397   g_free(enc);
  398 }
  399