Browse code

implement sending notification e-mails (with the corresponding config file support)

Dario Rodriguez authored on 24/07/2014 11:03:47
Showing 7 changed files
... ...
@@ -12,18 +12,22 @@ clean:
12 12
 kakumei: kakumei.o loglib.o parselib.o sbuf.o \
13 13
 		socklib.o webkernel.o gen_res.o \
14 14
 		kakumei_session.o kakumei_pass.o \
15
-		kakumei_posts.o kakumei_config.o
15
+		kakumei_posts.o kakumei_config.o \
16
+		kakumei_email.o
16 17
 	$(CC) $(LDFLAGS_KAKUMEI) kakumei.o loglib.o parselib.o sbuf.o \
17 18
 		socklib.o webkernel.o gen_res.o \
18 19
 		kakumei_session.o kakumei_pass.o \
19 20
 		kakumei_posts.o kakumei_config.o \
21
+		kakumei_email.o \
20 22
 		-o kakumei
21 23
 
22 24
 kakumei.o: kakumei.c ../src/gen_res.c
23 25
 
24 26
 ../src/gen_res.c: ../res/index.html  ../res/script.js ../res/style.css \
25 27
 		  ../res/newuser.html ../res/newuser.js ../res/newuser.css \
26
-		  ../res/posts.html ../res/posts.js ../res/posts.css
28
+		  ../res/posts.html ../res/posts.js ../res/posts.css \
29
+		  ../res/newpost.html ../res/newpost.js ../res/newpost.css \
30
+		  ../res/account.html ../res/account.js ../res/account.css
27 31
 	( cd ../res && ./gen.sh )
28 32
 
29 33
 gen_res.o: ../src/gen_res.c
... ...
@@ -24,6 +24,7 @@
24 24
 #include "kakumei_session.h"
25 25
 #include "kakumei_pass.h"
26 26
 #include "kakumei_posts.h"
27
+#include "kakumei_email.h"
27 28
 #include "kakumei_config.h"
28 29
 
29 30
 #define CONFIGFILE "kakumei.conf"
... ...
@@ -50,6 +51,8 @@ wk_action http_lastpost(wk *web, int connid, wk_uri *uri, void *userptr,char *us
50 51
 wk_action http_getpost(wk *web, int connid, wk_uri *uri, void *userptr,char *user);
51 52
 wk_action http_newpost(wk *web, int connid, wk_uri *uri, void *userptr,char *user);
52 53
 wk_action http_newcomment(wk *web, int connid, wk_uri *uri, void *userptr,char *user);
54
+wk_action http_changeemail(wk *web, int connid, wk_uri *uri, void *userptr,char *user);
55
+wk_action http_getemail(wk *web, int connid, wk_uri *uri, void *userptr,char *user);
53 56
 
54 57
 int
55 58
 main(int argc, char *argv[])
... ...
@@ -113,8 +116,8 @@ main(int argc, char *argv[])
113 116
         log_write("INIT","Server initialized, waiting connections...");
114 117
         while(!sigint_flag) {
115 118
                 sselect_wait(ka->ssel,timeout);
116
-		if(sigint_flag)
117
-			break;
119
+                if(sigint_flag)
120
+                        break;
118 121
                 wk_service(ka->web);
119 122
         }
120 123
         wk_free(ka->web),ka->web=NULL;
... ...
@@ -322,6 +325,10 @@ callback_http(wk *web, int connid, wk_uri *uri, void *userptr)
322 325
                         return(http_newpost(web,connid,uri,userptr,user));
323 326
                 else if(memcmp(uri->path,"/newcomment?",12)==0)
324 327
                         return(http_newcomment(web,connid,uri,userptr,user));
328
+                else if(memcmp(uri->path,"/changeemail?",13)==0)
329
+                        return(http_changeemail(web,connid,uri,userptr,user));
330
+                else if(memcmp(uri->path,"/getemail?",10)==0)
331
+                        return(http_getemail(web,connid,uri,userptr,user));
325 332
         }
326 333
         /* not found */
327 334
         log_write("HTTP","URI not found: %s",uri->path);
... ...
@@ -457,7 +464,6 @@ http_lastpost(wk *web, int connid, wk_uri *uri, void *userptr,char *user)
457 464
         wk_serve_buffer_as_file(web,connid,reply,strlen(reply),"text/plain");
458 465
         log_write("LSTP","Reply: %s",reply);
459 466
         return(wkact_finished);
460
-
461 467
 }
462 468
 
463 469
 wk_action
... ...
@@ -496,6 +502,7 @@ http_newpost(wk *web, int connid, wk_uri *uri, void *userptr,char *user)
496 502
                 log_write("NEWP","invalid post, send error");
497 503
                 return(wkact_finished);
498 504
         }
505
+        email_notify(ka,user,-1);
499 506
         log_write("NEWP","Replying with last post");
500 507
         return(http_lastpost(web,connid,uri,userptr,user));
501 508
 }
... ...
@@ -519,6 +526,41 @@ http_newcomment(wk *web, int connid, wk_uri *uri, void *userptr,char *user)
519 526
         }
520 527
         wk_serve_buffer_as_file(web,connid,n,strlen(n),"text/plain");
521 528
         log_write("NEWC","Reply: %s",n);
529
+        email_notify(ka,user,num);
530
+        return(wkact_finished);
531
+}
532
+
533
+wk_action
534
+http_changeemail(wk *web, int connid, wk_uri *uri, void *userptr,char *user)
535
+{
536
+        char useremail[128];
537
+        kakumei *ka=(kakumei *)userptr;
538
+        if(wk_uri_copyvar(uri,"a",useremail,sizeof(useremail))==NULL)
539
+                useremail[0]='\0';
540
+        if(email_set(ka,user,useremail)!=0) {
541
+                wk_serve_error(web,connid,wkerr_internal);
542
+                log_write("EMCH","couldn't change email, send error");
543
+                return(wkact_finished);
544
+        }
545
+        /* valid */
546
+        wk_serve_buffer_as_file(web,connid,useremail,strlen(useremail),"text/plain");
547
+        log_write("EMCH","Reply: %s",useremail);
548
+        return(wkact_finished);
549
+}
550
+
551
+wk_action
552
+http_getemail(wk *web, int connid, wk_uri *uri, void *userptr,char *user)
553
+{
554
+        char useremail[128];
555
+        kakumei *ka=(kakumei *)userptr;
556
+        if(email_get(ka,user,useremail,sizeof(useremail))!=0) {
557
+                wk_serve_error(web,connid,wkerr_internal);
558
+                log_write("EMGT","no user email, send error");
559
+                return(wkact_finished);
560
+        }
561
+        /* valid */
562
+        wk_serve_buffer_as_file(web,connid,useremail,strlen(useremail),"text/plain");
563
+        log_write("EMGT","Reply: %s",useremail);
522 564
         return(wkact_finished);
523 565
 }
524 566
 
... ...
@@ -6,7 +6,7 @@
6 6
  * Header file
7 7
  *
8 8
  * Author: Dario Rodriguez dario@softhome.net
9
- * This progran is licensed under the terms of the Affero GPL v1+
9
+ * This program is licensed under the terms of the Affero GPL v1+
10 10
  */
11 11
 
12 12
 #ifndef KAKUMEI_H
... ...
@@ -19,6 +19,13 @@
19 19
 #include "kakumei_config.h"
20 20
 
21 21
 #define MAXLINESIZE 2048
22
+#define LINESBLOCK 32
23
+
24
+typedef enum esection {
25
+        section_none=0,
26
+        section_general,
27
+        section_mail
28
+} esection;
22 29
 
23 30
 kaconfig *
24 31
 kaconfig_init(char *configfile)
... ...
@@ -28,7 +35,7 @@ kaconfig_init(char *configfile)
28 35
         char line[MAXLINESIZE];
29 36
         int len;
30 37
         char *ptr,*sep,*value;
31
-        int generalsection;
38
+        esection section;
32 39
         int lineno;
33 40
         char **configvalue;
34 41
         if((f=fopen(configfile,"r"))==NULL)
... ...
@@ -38,15 +45,22 @@ kaconfig_init(char *configfile)
38 45
                 return(NULL);
39 46
         }
40 47
         memset(config,0,sizeof(kaconfig));
41
-        generalsection=0;
48
+        section=section_none;
42 49
         lineno=0;
43 50
         while(fgets(line,sizeof(line),f)!=NULL) {
44 51
                 lineno++;
45 52
                 line[sizeof(line)-1]='\0';
46 53
                 /* trim line */
54
+                for(ptr=line;*ptr!='\0' && strchr("\t ",*ptr)!=NULL;ptr++)
55
+                        ;
47 56
                 len=strlen(line);
48
-                while(len>1 && strchr("\n\t ",line[len-1])!=NULL)
49
-                        line[len-1]='\0',len--;
57
+                if(!(section==section_mail && memcmp(ptr,"line.",5)==0)) {
58
+                        while(len>0 && strchr("\n\t ",line[len-1])!=NULL)
59
+                                line[len-1]='\0',len--;
60
+                } else {
61
+                        while(len>0 && line[len-1]=='\n')
62
+                                line[len-1]='\0',len--;
63
+                }
50 64
                 for(ptr=line;*ptr!='\0' && strchr("\t ",*ptr)!=NULL;ptr++)
51 65
                         ;
52 66
                 len=strlen(ptr);
... ...
@@ -56,11 +70,13 @@ kaconfig_init(char *configfile)
56 70
                 /* parse line */
57 71
                 if(*ptr=='[' && ptr[len-1]==']') {
58 72
                         /* section */
59
-                        if(strcmp(ptr,"[general]")==0)
60
-                                generalsection=1;
61
-                        else {
73
+                        if(strcmp(ptr,"[general]")==0) {
74
+                                section=section_general;
75
+                        } else if(strcmp(ptr,"[mail]")==0) {
76
+                                section=section_mail;
77
+                        } else {
62 78
                                 log_write("CONF","%s:%i: unknown section name \"%s\"\n",configfile,lineno,ptr);
63
-                                generalsection=0;
79
+                                section=section_none;
64 80
                         }
65 81
                         continue;
66 82
                 } else if((sep=strchr(ptr,'='))!=NULL) {
... ...
@@ -71,38 +87,77 @@ kaconfig_init(char *configfile)
71 87
                                 sep--;
72 88
                                 *sep='\0';
73 89
                         }
74
-                        /* trim value */
75
-                        while(*value!='\0' && strchr(" \t",*value)!=NULL)
76
-                                value++;
90
+                        if(!(section==section_mail && memcmp(ptr,"line.",5)==0)) {
91
+                                /* trim value */
92
+                                while(*value!='\0' && strchr(" \t",*value)!=NULL)
93
+                                        value++;
94
+                        }
77 95
                         /* sanity check */
78 96
                         if(*value=='\0') {
79
-                                log_write("CONF","%s:%i: ignoring key without value; key:\"%s\"\n",configfile,lineno,ptr);
97
+                                if(!(section==section_mail && memcmp(ptr,"line.",5)==0))
98
+                                        log_write("CONF","%s:%i: ignoring key without value; key:\"%s\"\n",configfile,lineno,ptr);
80 99
                                 continue;
81 100
                         }
82 101
                         /* assign value */
83
-                        if(generalsection!=1) {
102
+                        if(section==section_none) {
84 103
                                 log_write("CONF","%s:%i: ignoring key-value pair in unknown section; key:\"%s\"\n",configfile,lineno,ptr);
85 104
                                 continue;
86 105
                         }
87 106
                         /* identify key-value pair */
88
-                        if(strcmp(ptr,"logfile")==0) {
89
-                                configvalue=&(config->logfile);
90
-                        } else if(strcmp(ptr,"cookiename")==0) {
91
-                                configvalue=&(config->cookiename);
92
-                        } else if(strcmp(ptr,"cookiedomain")==0) {
93
-                                configvalue=&(config->cookiedomain);
94
-                        } else if(strcmp(ptr,"bannerpath")==0) {
95
-                                configvalue=&(config->bannerpath);
96
-                        } else if(strcmp(ptr,"sslproxy")==0) {
97
-				if(strcmp(value,"true")==0 || strcmp(value,"1")==0 || strcmp(value,"yes")==0)
98
-					config->sslproxy=1;
99
-				else
100
-					config->sslproxy=0;
101
-				continue;
102
-                        } else {
103
-                                log_write("CONF","%s:%i: unknown key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
107
+                        if(section==section_general) {
108
+                                if(strcmp(ptr,"logfile")==0) {
109
+                                        configvalue=&(config->logfile);
110
+                                } else if(strcmp(ptr,"cookiename")==0) {
111
+                                        configvalue=&(config->cookiename);
112
+                                } else if(strcmp(ptr,"cookiedomain")==0) {
113
+                                        configvalue=&(config->cookiedomain);
114
+                                } else if(strcmp(ptr,"bannerpath")==0) {
115
+                                        configvalue=&(config->bannerpath);
116
+                                } else if(strcmp(ptr,"sslproxy")==0) {
117
+                                        if(strcmp(value,"true")==0 || strcmp(value,"1")==0 || strcmp(value,"yes")==0)
118
+                                                config->sslproxy=1;
119
+                                        else
120
+                                                config->sslproxy=0;
121
+                                        continue;
122
+                                } else {
123
+                                        log_write("CONF","%s:%i: unknown key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
124
+                                        continue;
125
+                                }
126
+                        } else if(section==section_mail) {
127
+                                if(strcmp(ptr,"from")==0) {
128
+                                        configvalue=&(config->from);
129
+                                } else if(strcmp(ptr,"subjpost")==0) {
130
+                                        configvalue=&(config->subjpost);
131
+                                } else if(strcmp(ptr,"subjcomment")==0) {
132
+                                        configvalue=&(config->subjcomment);
133
+                                } else if(memcmp(ptr,"line.",5)==0) {
134
+                                        int lineno=atoi(ptr+5);
135
+                                        if(lineno<0)
136
+                                                lineno=0;
137
+                                        if(lineno>=config->sizelines) {
138
+                                                char **lines;
139
+                                                int minsize;
140
+                                                minsize=(lineno+LINESBLOCK)/LINESBLOCK;
141
+                                                minsize*=LINESBLOCK;
142
+                                                if((lines=realloc(config->lines,minsize*sizeof(char *)))==NULL) {
143
+                                                        fclose(f),f=NULL;
144
+                                                        kaconfig_free(config),config=NULL;
145
+                                                        return(NULL); /* insufficient memory */
146
+                                                }
147
+                                                config->lines=lines;
148
+                                                memset(lines+config->sizelines,0,sizeof(char *)*(minsize-config->sizelines));
149
+                                                config->sizelines=minsize;
150
+                                        }
151
+                                        configvalue=config->lines+lineno;
152
+                                        if(config->usedlines<=lineno)
153
+                                                config->usedlines=lineno+1;
154
+                                } else {
155
+                                        log_write("CONF","%s:%i: unknown key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
156
+                                        continue;
157
+                                }
158
+
159
+                        } else
104 160
                                 continue;
105
-                        }
106 161
                         /* store value */
107 162
                         if(*configvalue!=NULL) {
108 163
                                 log_write("CONF","%s:%i: duplicate key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
... ...
@@ -137,6 +192,22 @@ kaconfig_free(kaconfig *config)
137 192
                 free(config->cookiedomain),config->cookiedomain=NULL;
138 193
         if(config->bannerpath!=NULL)
139 194
                 free(config->bannerpath),config->bannerpath=NULL;
195
+        if(config->from!=NULL)
196
+                free(config->from),config->from=NULL;
197
+        if(config->subjpost!=NULL)
198
+                free(config->subjpost),config->subjpost=NULL;
199
+        if(config->subjcomment!=NULL)
200
+                free(config->subjcomment),config->subjcomment=NULL;
201
+        if(config->lines!=NULL) {
202
+                int i;
203
+                for(i=0;i<config->usedlines;i++) {
204
+                        if(config->lines[i]!=NULL)
205
+                                free(config->lines[i]),config->lines[i]=NULL;
206
+                }
207
+                config->sizelines=0;
208
+                config->usedlines=0;
209
+                free(config->lines),config->lines=NULL;
210
+        }
140 211
         free(config),config=NULL;
141 212
         return;
142 213
 }
... ...
@@ -163,6 +234,7 @@ kaconfig_write(char *configfile,char *logfile, char *cookiename,char *cookiedoma
163 234
         fprintf(f,"cookiedomain=%s\n",(cookiedomain!=NULL)?cookiedomain:"localhost");
164 235
         fprintf(f,"bannerpath=%s\n",(bannerpath!=NULL)?bannerpath:"default.png");
165 236
         fprintf(f,"sslproxy=%s\n",(sslproxy==0)?"no":"yes");
237
+        fprintf(f,"\n[mail]\nfrom=\nsubjpost=New post\nsubjcomment=New comment in post\nline.0=There is a new post or comment\nline.1=Best regards, kakumei web\n");
166 238
         return(0);
167 239
 }
168 240
 
... ...
@@ -12,11 +12,19 @@
12 12
 #ifndef KAKUMEI_CONFIG_H
13 13
 #define KAKUMEI_CONFIG_H
14 14
 typedef struct kaconfig {
15
+        /* general */
15 16
         char *logfile;
16 17
         char *cookiename;
17 18
         char *cookiedomain;
18 19
         char *bannerpath;
19 20
         int sslproxy;
21
+        /* mail */
22
+        char *from;
23
+        char *subjpost;
24
+        char *subjcomment;
25
+        int sizelines;
26
+        int usedlines;
27
+        char **lines;
20 28
 } kaconfig;
21 29
 
22 30
 kaconfig *kaconfig_init(char *configfile);
23 31
new file mode 100644
... ...
@@ -0,0 +1,219 @@
1
+/*
2
+ * kakumei_email.c
3
+ *
4
+ * E-mail management
5
+ *
6
+ * Author: Dario Rodriguez dario@softhome.net
7
+ * This program is licensed under the terms of the Affero GPL v1+
8
+ */
9
+
10
+#include <stdio.h>
11
+#include <stdlib.h>
12
+#include <unistd.h>
13
+#include <string.h>
14
+#include <fcntl.h>
15
+#include <sys/types.h>
16
+#include <sys/stat.h>
17
+#include <dirent.h>
18
+#include "sbuf.h"
19
+#include "loglib.h"
20
+#include "kakumei.h"
21
+#include "kakumei_email.h"
22
+
23
+#define RD 0
24
+#define WR 1
25
+
26
+static int email_openprogram(kakumei *ka, char *program,char **args, int *parent2child, int *child2parent);
27
+static char **email_sendmailargs(kakumei *ka, sbuf *buf, char *fromuser);
28
+static int writestr(int fd, char *ptr);
29
+
30
+int
31
+email_set(kakumei *ka, char *user, char *email)
32
+{
33
+        char filename[1024];
34
+        int fd;
35
+        if(ka==NULL || user==NULL || user[0]=='\0' || email==NULL) {
36
+                log_write("MAIL","sanity check error");
37
+                return(-1);
38
+        }
39
+        mkdir(USERSDIR,0700);
40
+        snprintf(filename,sizeof(filename)-1,"%s/%s/email",USERSDIR,user);
41
+        filename[sizeof(filename)-1]='\0';
42
+        if(email[0]=='\0') {
43
+                unlink(filename);
44
+                return(0);
45
+        }
46
+        if((fd=open(filename,O_WRONLY|O_TRUNC|O_CREAT,0600))==-1)
47
+                return(-1);
48
+        write(fd,email,strlen(email));
49
+        close(fd),fd=-1;
50
+        return(0);
51
+}
52
+
53
+int
54
+email_get(kakumei *ka, char *user, char *email,int sizeemail)
55
+{
56
+        char filename[1024];
57
+        int fd;
58
+        if(ka==NULL || user==NULL || user[0]=='\0' || email==NULL || sizeemail<1) {
59
+                log_write("MAIL","sanity check error");
60
+                return(-1);
61
+        }
62
+        snprintf(filename,sizeof(filename)-1,"%s/%s/email",USERSDIR,user);
63
+        filename[sizeof(filename)-1]='\0';
64
+        if((fd=open(filename,O_RDONLY))==-1)
65
+                return(-1);
66
+        memset(email,0,sizeemail);
67
+        read(fd,email,sizeemail-1);
68
+        email[sizeemail-1]='\0';
69
+        close(fd),fd=-1;
70
+        return(0);
71
+}
72
+
73
+int
74
+email_notify(kakumei *ka, char *fromuser, int numpost)
75
+{
76
+        int bufnum;
77
+        sbuf *buf;
78
+        int parent2child,child2parent;
79
+        char **args;
80
+        int i;
81
+        /* check if from has been defined in config file */
82
+        if(ka->config->from==NULL) {
83
+                log_write("MAIL","mail isn't configured in kakumei config file (no \"from\")");
84
+                return(-1);
85
+        }
86
+        /* generate the list of recipients */
87
+        if((bufnum=wk_sbufacquire(ka->web))==-1) {
88
+                log_write("MAIL","couldn't acquire buffer to prepare the recipients list");
89
+                return(-1);
90
+        }
91
+        if((buf=wk_sbufget(ka->web,bufnum))==NULL) {
92
+                log_write("EINT","%s:%i",__FILE__,__LINE__);
93
+                return(-1);
94
+        }
95
+        if((args=email_sendmailargs(ka,buf,fromuser))==NULL) {
96
+                wk_sbufrelease(ka->web,bufnum),bufnum=-1;
97
+                log_write("MAIL","couldn't build recipient list");
98
+                return(-1);
99
+        }
100
+        /* send the message */
101
+        if(email_openprogram(ka,"/usr/sbin/sendmail",args,&parent2child,&child2parent)<=0) {
102
+                wk_sbufrelease(ka->web,bufnum),bufnum=-1;
103
+                log_write("MAIL","couldn't exec sendmail");
104
+                return(-1);
105
+        }
106
+        wk_sbufrelease(ka->web,bufnum),bufnum=-1;
107
+        writestr(parent2child,"From: ");
108
+        writestr(parent2child,(ka->config->from!=NULL)?ka->config->from:"");
109
+        writestr(parent2child,"\nSubject: ");
110
+        if(numpost==-1)
111
+                writestr(parent2child,(ka->config->subjpost!=NULL)?ka->config->subjpost:"kakumei post");
112
+        else
113
+                writestr(parent2child,(ka->config->subjcomment!=NULL)?ka->config->subjcomment:"kakumei comment");
114
+        if(numpost!=-1) {
115
+                char mybuf[32];
116
+                snprintf(mybuf,sizeof(mybuf)," #%i",numpost);
117
+                mybuf[sizeof(mybuf)-1]='\0';
118
+                writestr(parent2child,mybuf);
119
+        }
120
+        writestr(parent2child,"\n\n");
121
+        for(i=0;i<ka->config->usedlines;i++) {
122
+                writestr(parent2child,(ka->config->lines[i]==NULL)?"":ka->config->lines[i]);
123
+                writestr(parent2child,"\n");
124
+        }
125
+        close(parent2child),parent2child=-1;
126
+        close(child2parent),child2parent=-1;
127
+        log_write("MAIL","sent email notify from user:%s post:%i",fromuser,numpost);
128
+        return(0);
129
+}
130
+
131
+static int
132
+email_openprogram(kakumei *ka, char *program,char **args, int *paramparent2child, int *paramchild2parent)
133
+{
134
+        int parent2child[2];
135
+        int child2parent[2];
136
+        int pid;
137
+        parent2child[0]=parent2child[1]=-1;
138
+        child2parent[0]=child2parent[1]=-1;
139
+        if(pipe(parent2child)!=0 || pipe(child2parent)!=0) {
140
+                close(parent2child[0]); /* just in case the first pipe() succeeded but not the second */
141
+                close(parent2child[1]);
142
+                return(-1); /* Couldn't create pipes for communicating with child process */
143
+        }
144
+        if((pid=fork())==0) {
145
+                /* child process */
146
+                int i,l;
147
+                close(0);
148
+                dup(parent2child[RD]);
149
+                close(1);
150
+                dup(child2parent[WR]);
151
+                l=getdtablesize();
152
+                for(i=2;i<l;i++)
153
+                        close(i);
154
+                execv(program,args);
155
+                exit(1); /* error in exec */
156
+        }
157
+        close(parent2child[RD]),parent2child[RD]=-1;
158
+        close(child2parent[WR]),child2parent[WR]=-1;
159
+        *paramparent2child=parent2child[WR];
160
+        *paramchild2parent=child2parent[RD];
161
+        return(pid);
162
+}
163
+
164
+static char **
165
+email_sendmailargs(kakumei *ka, sbuf *buf, char *fromuser)
166
+{
167
+        DIR *dir;
168
+        struct dirent *de;
169
+        int pre;
170
+        int n,k;
171
+        char *ptr;
172
+        char **args;
173
+        char email[128];
174
+        sbuf_wipe(buf);
175
+        pre=2;
176
+        sbuf_addstr(buf,"sendmail");
177
+        sbuf_add(buf,"",1);
178
+        sbuf_addstr(buf,"-i");
179
+        sbuf_add(buf,"",1);
180
+        if((dir=opendir(USERSDIR))==NULL) {
181
+                log_write("MAIL","couldn't open users directory");
182
+                return(NULL);
183
+        }
184
+        for(n=0;(de=readdir(dir))!=NULL;) {
185
+                if(strcmp(de->d_name,fromuser)==0)
186
+                        continue;
187
+                if(email_get(ka,de->d_name,email,sizeof(email))!=0)
188
+                        continue;
189
+                uri_urldecode(email);
190
+                sbuf_add(buf,email,strlen(email)+1);
191
+                n++;
192
+        }
193
+        closedir(dir),dir=NULL;
194
+        if(n==0) {
195
+                log_write("MAIL","no recipients for e-mail notification");
196
+                return(NULL); /* no recipients */
197
+        }
198
+        n+=pre;
199
+        if(sbuf_unused(buf)<(sizeof(char *)*(n+1))) {
200
+                log_write("MAIL","too many recipients, couldn't build list");
201
+                return(NULL); /* too many recipients */
202
+        }
203
+        args=(char **)sbuf_ptrunused(buf);
204
+        memset((char *)args,0,(sizeof(char *)*(n+1)));
205
+        for(k=0,ptr=sbuf_ptr(buf);k<n;k++) {
206
+                args[k]=ptr;
207
+                ptr+=strlen(ptr)+1;
208
+        }
209
+        args[k]=NULL;
210
+        sbuf_addfromunused(buf,(sizeof(char *)*(n+1)));
211
+        return(args);
212
+}
213
+
214
+static int
215
+writestr(int fd, char *ptr)
216
+{
217
+        return(write(fd,ptr,strlen(ptr)));
218
+}
219
+
0 220
new file mode 100644
... ...
@@ -0,0 +1,17 @@
1
+/*
2
+ * kakumei_email.c
3
+ *
4
+ * E-mail management
5
+ *
6
+ * Header file.
7
+ *
8
+ * Author: Dario Rodriguez dario@softhome.net
9
+ * This program is licensed under the terms of the Affero GPL v1+
10
+ */
11
+
12
+#include "kakumei.h"
13
+
14
+int email_set(kakumei *ka, char *user, char *email);
15
+int email_get(kakumei *ka, char *user, char *email,int sizeemail);
16
+int email_notify(kakumei *ka, char *fromuser, int numpost);
17
+