1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <fcntl.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <pthread.h>
10 #include <array.h>
11 #include <hash.h>
12 #include <perl.h>
13 #include <setup.h>
14 #include <memory.h>
15 #include <notify.h>
16 #include <cookies.h>
17 
18 typedef struct NODE {
19   size_t threadID;
20   COOKIE cookie;
21   struct NODE *next;
22 } NODE;
23 
24 struct COOKIES_T {
25   NODE *          head;
26   size_t          size;
27   char *          file;
28 };
29 
30 private NODE *  __delete_node(NODE *node);
31 private BOOLEAN __exists(char *file);
32 private BOOLEAN __save_cookies(COOKIES this);
33 
34 COOKIES
new_cookies()35 new_cookies() {
36   int len;
37   COOKIES this;
38   char    name[] = "/.siege/cookies.txt";
39 
40   this = calloc(sizeof(struct COOKIES_T), 1);
41   this->size = 0;
42   char *p = getenv("HOME");
43   len = p ? strlen(p) : 60;
44   len += strlen(name)+1;
45   this->file = xmalloc(sizeof (char*) * len);
46   memset(this->file, '\0', len);
47   snprintf(this->file, len, "%s%s", getenv("HOME"), name);
48   return this;
49 }
50 
51 COOKIES
cookies_destroy(COOKIES this)52 cookies_destroy(COOKIES this)
53 {
54   NODE *cur     = NULL;
55   __save_cookies(this);
56   cur = this->head;
57   while (cur) {
58     cur = __delete_node(cur);
59   }
60   xfree(this->file);
61   free(this);
62   return NULL;
63 }
64 
65 BOOLEAN
cookies_add(COOKIES this,char * str,char * host)66 cookies_add(COOKIES this, char *str, char *host)
67 {
68   size_t  id    = (uintptr_t)pthread_self();
69   int     hlen  = 0;
70   int     dlen  = 0;
71   NODE   *cur   = NULL;
72   NODE   *pre   = NULL;
73   NODE   *new   = NULL;
74   BOOLEAN found = FALSE;
75   BOOLEAN valid = FALSE;
76   COOKIE  oreo  = new_cookie(str, host);
77 
78   if (oreo == NULL) return FALSE;
79   if (cookie_get_name(oreo) == NULL || cookie_get_value(oreo) == NULL) return FALSE;
80 
81   //pthread_mutex_lock(&(my.lock));
82   for (cur = pre = this->head; cur != NULL; pre = cur, cur = cur->next) {
83     const char *domainptr = cookie_get_domain(cur->cookie);
84     if (*domainptr == '.') ++domainptr;
85     hlen = host      ? strlen(host)      : 0;
86     dlen = domainptr ? strlen(domainptr) : 0;
87     if (! strcasecmp(host, domainptr)) {
88       valid = TRUE; // host level cookie found
89     }
90     if (! valid && (dlen < hlen) && (! strcasecmp(host + (hlen - dlen), domainptr))) {
91       valid = TRUE; // domain level cookie found
92     }
93     if (valid && cur->threadID == id && !strcasecmp(cookie_get_name(cur->cookie), cookie_get_name(oreo))) {
94       cookie_reset_value(cur->cookie, cookie_get_value(oreo));
95       oreo  = cookie_destroy(oreo);
96       found = TRUE;
97       break;
98     }
99   }
100 
101   if (!found) {
102     new = (NODE*)malloc(sizeof(NODE));
103     new->threadID = id;
104     new->cookie   = oreo;
105     new->next     = cur;
106     if (cur == this->head)
107       this->head = new;
108     else
109       pre->next  = new;
110   }
111   //pthread_cond_wait(&my.cond, &my.lock);
112   //pthread_mutex_unlock(&(my.lock));
113 
114   return TRUE;
115 }
116 
117 BOOLEAN
cookies_delete(COOKIES this,char * str)118 cookies_delete(COOKIES this, char *str)
119 {
120   NODE     *cur;
121   NODE     *pre;
122   BOOLEAN   ret = FALSE;
123   pthread_t id  = pthread_self();
124 
125   for (cur = pre = this->head; cur != NULL; pre = cur, cur = cur->next) {
126     if (cur->threadID == id) {
127       char *name    = cookie_get_name(cur->cookie);
128       if (!strcasecmp(name, str)) {
129         cur->cookie = cookie_destroy(cur->cookie);
130         pre->next   = cur->next;
131         if (cur == this->head) {
132           this->head = cur->next;
133           pre = this->head;
134         } else {
135           pre->next = cur->next;
136         }
137         ret = TRUE;
138         break;
139       }
140     }
141   }
142   return ret;
143 }
144 
145 BOOLEAN
cookies_delete_all(COOKIES this)146 cookies_delete_all(COOKIES this)
147 {
148   NODE     *cur;
149   NODE     *pre;
150   pthread_t id  = pthread_self();
151 
152   // XXX: delete cookies by thread; not every cookie in the list
153   for (cur = pre = this->head; cur != NULL; pre = cur, cur = cur->next) {
154     if (cur->threadID == id) {
155       //char *name    = cookie_get_name(cur->cookie);
156       cur->cookie = cookie_destroy(cur->cookie);
157       pre->next   = cur->next;
158       if (cur == this->head) {
159         this->head = cur->next;
160         pre = this->head;
161       } else {
162         pre->next = cur->next;
163       }
164     }
165   }
166   return TRUE;
167 }
168 
169 char *
cookies_header(COOKIES this,char * host,char * newton)170 cookies_header(COOKIES this, char *host, char *newton)
171 {
172   int   dlen;
173   int   hlen;
174   NODE  *pre;
175   NODE  *cur;
176   time_t tmp;
177   time_t now;
178   struct tm tm;
179   char   oreo[MAX_COOKIES_SIZE];
180   size_t id = (uintptr_t)pthread_self();
181 
182   memset(oreo, '\0', sizeof oreo);
183   hlen = strlen(host);
184 
185   tmp = time(NULL);
186   gmtime_r(&tmp, &tm);
187   tm.tm_isdst = -1; // force mktime to figure it out!
188   now = mktime(&tm);
189 
190   for (cur=pre=this->head; cur != NULL; pre=cur, cur=cur->next) {
191     /**
192      * for the purpose of matching, we'll ignore the leading '.'
193      */
194     const char *domainptr = cookie_get_domain(cur->cookie);
195     if (*domainptr == '.') ++domainptr;
196     dlen = domainptr ? strlen(domainptr) : 0;
197     if (cur->threadID == id) {
198       if (!strcasecmp(domainptr, host)) {
199         if (cookie_get_expires(cur->cookie) <= now && cookie_get_session(cur->cookie) != TRUE) {
200           cookies_delete(this, cookie_get_name(cur->cookie));
201           continue;
202         }
203         if (strlen(oreo) > 0)
204           strncat(oreo, ";",      sizeof(oreo) - 10 - strlen(oreo));
205         strncat(oreo, cookie_get_name(cur->cookie),  sizeof(oreo) - 10 - strlen(oreo));
206         strncat(oreo, "=",        sizeof(oreo) - 10 - strlen(oreo));
207         strncat(oreo, cookie_get_value(cur->cookie), sizeof(oreo) - 10 - strlen(oreo));
208       }
209       if ((dlen < hlen) && (!strcasecmp(host + (hlen - dlen), domainptr))) {
210         if (cookie_get_expires(cur->cookie) <= now && cookie_get_session(cur->cookie) != TRUE) {
211           cookies_delete(this, cookie_get_name(cur->cookie));
212           continue;
213         }
214         if (strlen(oreo) > 0)
215           strncat(oreo, ";",      sizeof(oreo) - 10 - strlen(oreo));
216         strncat(oreo, cookie_get_name(cur->cookie),  sizeof(oreo) - 10 - strlen(oreo));
217         strncat(oreo, "=",        sizeof(oreo) - 10 - strlen(oreo));
218         strncat(oreo, cookie_get_value(cur->cookie), sizeof(oreo) - 10 - strlen(oreo));
219       }
220     }
221   }
222   if (strlen(oreo) > 0) {
223     strncpy(newton, "Cookie: ", 8);
224     strncat(newton, oreo,       MAX_COOKIE_SIZE);
225     strncat(newton, "\015\012", 2);
226   }
227 
228   return newton;
229 }
230 
231 void
cookies_list(COOKIES this)232 cookies_list(COOKIES this)
233 {
234   NODE *cur     = NULL;
235   NODE *pre     = NULL;
236 
237   for (cur = pre = this->head; cur != NULL; pre = cur, cur = cur->next) {
238     COOKIE tmp = cur->cookie;
239     if (tmp == NULL)
240       ;
241     else printf(
242       "%lld: NAME: %s\n   VALUE: %s\n   Expires: %s\n",
243       (long long)cur->threadID, cookie_get_name(tmp), cookie_get_value(tmp), cookie_expires_string(tmp)
244     );
245   }
246 }
247 
248 private NODE *
__delete_node(NODE * node)249 __delete_node(NODE *node)
250 {
251 
252   if (node == NULL) return NULL;
253   NODE *tmp = node->next;
254   node->cookie = cookie_destroy(node->cookie);
255   free(node);
256   node = tmp;
257   return node;
258 }
259 
260 private void
__strip(char * str)261 __strip(char *str)
262 {
263   char *ch;
264   ch = (char *)strstr(str, "#");
265   if (ch){*ch = '\0';}
266   ch = (char *)strstr(str, "\n");
267   if (ch){*ch = '\0';}
268 }
269 
270 HASH
load_cookies(COOKIES this)271 load_cookies(COOKIES this)
272 {
273   FILE * fp;
274   int    n = -1;
275   HASH   HOH;
276   HASH   IDX;
277   const  size_t len = 4096; // max cookie size
278   char   line[len];
279 
280   if (! __exists(this->file)) {
281     return NULL;
282   }
283 
284   fp = fopen(this->file, "r");
285   if (fp == NULL) {
286     return NULL;
287   }
288 
289   HOH = new_hash();
290   IDX = new_hash();
291 
292   /**
293    * We're going to treat this file like it's editable
294    * which means it will permit comments and white space
295    * formatting. Siege users are a savvy bunch, they may
296    * want to add cookies by hand...
297    */
298   memset(line, '\0', len);
299   while (fgets(line, len, fp) != NULL){
300     char *p = strchr(line, '\n');
301     if (p) {
302       *p = '\0';
303     } else {
304       int  i;
305       if ((i = fgetc(fp)) != EOF) {
306         while ((i = fgetc(fp)) != EOF && i != '\n');
307         line[0]='\0';
308       }
309     }
310     __strip(line);
311     chomp(line);
312     if (strlen(line) > 1) {
313       int   num = 2;
314       char  **pair;
315       pair = split('|', line, &num);
316       trim(pair[0]);
317       trim(pair[1]);
318       if (pair[0] != NULL && pair[1] != NULL) {
319         if (hash_get(IDX, pair[0]) == NULL) {
320           char tmp[1024];
321           n += 1;
322           memset(tmp, '\0', 1024);
323           snprintf(tmp, 1024, "%d", n);
324           hash_add(IDX, pair[0], tmp);
325         }
326         HASH tmp = (HASH)hash_get(HOH, hash_get(IDX, pair[0]));
327         if (tmp == NULL) {
328           tmp = new_hash();
329           hash_add(tmp, pair[1], pair[1]);
330           hash_nadd(HOH, hash_get(IDX, pair[0]), tmp, HASHSIZE);
331         } else {
332           hash_add(tmp, pair[1], pair[1]);
333         }
334       }
335       split_free(pair, num);
336     }
337     memset(line, '\0', len);
338   }
339   fclose(fp);
340   hash_destroy(IDX);
341   return HOH;
342 }
343 
344 private BOOLEAN
__save_cookies(COOKIES this)345 __save_cookies(COOKIES this)
346 {
347   FILE * fp;
348   char * line;
349   size_t len  = 4096+24; // max cookie size plus ID
350   time_t now;
351 
352   NODE * cur  = NULL;
353   now = time(NULL);
354   fp  = fopen(this->file, "w");
355   if (fp == NULL) {
356     fprintf(stderr, "ERROR: Unable to open cookies file: %s\n", this->file);
357     return FALSE;
358   }
359   fputs("#\n", fp);
360   fputs("# Siege cookies file. You may edit this file to add cookies\n",fp);
361   fputs("# manually but comments and formatting will be removed.    \n",fp);
362   fputs("# All cookies that expire in the future will be preserved. \n",fp);
363   fputs("# ---------------------------------------------------------\n",fp);
364   line = malloc(sizeof(char *) * len);
365 
366   for (cur = this->head; cur != NULL; cur = cur->next) {
367     COOKIE tmp = cur->cookie;
368     /**
369      * Criteria for saving cookies:
370      * 1.) It's not null (obvs)
371      * 2.) It's not a session cookie
372      * 3.) It's not expired.
373      * All cookies which meet the requirement are stored
374      * whether they were used during this session or not.
375      */
376     if (tmp != NULL && cookie_get_session(tmp) != TRUE && cookie_get_expires(cur->cookie) >= now) {
377       memset(line, '\0', len);
378       if (cookie_to_string(tmp) != NULL) {
379         snprintf(line, len, "%ld | %s\n", cur->threadID, cookie_to_string(tmp));
380       }
381       fputs(line, fp);
382     }
383   }
384   free(line);
385   fclose(fp);
386   return TRUE;
387 }
388 
389 /**
390  * returns TRUE if the file exists,
391  */
392 private BOOLEAN
__exists(char * file)393 __exists(char *file)
394 {
395   int  fd;
396 
397   if ((fd = open(file, O_RDONLY)) < 0) {
398     /**
399      * The file does NOT exist so the descriptor is -1
400      * No need to close it.
401      */
402     return FALSE;
403   } else {
404     /**
405      * Party on Garth...
406      */
407     close(fd);
408     return TRUE;
409   }
410   return FALSE;
411 }
412 
413