1 /*
2 * $Id: qmailadmin.c,v 1.6.2.16 2009/05/02 19:13:29 tomcollins Exp $
3 * Copyright (C) 1999-2004 Inter7 Internet Technologies, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <pwd.h>
27 #include <dirent.h>
28 #include <vpopmail_config.h>
29 /* undef some macros that get redefined in config.h below */
30 #undef PACKAGE_NAME
31 #undef PACKAGE_STRING
32 #undef PACKAGE_TARNAME
33 #undef PACKAGE_VERSION
34
35 #include <vpopmail.h>
36 #include <vauth.h>
37 #include <vlimits.h>
38
39 #include "alias.h"
40 #include "auth.h"
41 #include "autorespond.h"
42 #include "cgi.h"
43 #include "command.h"
44 #include "config.h"
45 #include "limits.h"
46 #include "mailinglist.h"
47 #include "printh.h"
48 #include "qmailadmin.h"
49 #include "show.h"
50 #include "template.h"
51 #include "user.h"
52 #include "util.h"
53
54 char Username[MAX_BUFF];
55 char Domain[MAX_BUFF];
56 char Password[MAX_BUFF];
57 char Gecos[MAX_BUFF];
58 char Quota[MAX_BUFF];
59 char Time[MAX_BUFF];
60 char ActionUser[MAX_BUFF];
61 char Newu[MAX_BUFF];
62 char Password1[MAX_BUFF];
63 char Password2[MAX_BUFF];
64 char Crypted[MAX_BUFF];
65 char Alias[MAX_BUFF];
66 char LineData[MAX_BUFF];
67 char Action[MAX_BUFF];
68 char Message[MAX_BIG_BUFF];
69 char StatusMessage[MAX_BIG_BUFF];
70 int num_of_mailinglist;
71 int CGIValues[256];
72 char Pagenumber[MAX_BUFF];
73 char SearchUser[MAX_BUFF];
74 time_t Mytime;
75 char *TmpCGI = NULL;
76 char TmpBuf[MAX_BIG_BUFF];
77 char TmpBuf1[MAX_BUFF];
78 char TmpBuf2[MAX_BUFF];
79 char TmpBuf3[MAX_BUFF];
80 char TempBuf[MAX_BUFF];
81 int Compressed;
82 FILE *actout;
83 FILE *lang_fs;
84 FILE *color_table;
85 char *html_text[MAX_LANG_STR+1];
86
87 struct vlimits Limits;
88 int AdminType;
89 int MaxPopAccounts;
90 int MaxAliases;
91 int MaxForwards;
92 int MaxAutoResponders;
93 int MaxMailingLists;
94
95 int DisablePOP;
96 int DisableIMAP;
97 int DisableDialup;
98 int DisablePasswordChanging;
99 int DisableWebmail;
100 int DisableRelay;
101
102 int CurPopAccounts;
103 int CurForwards;
104 int CurBlackholes;
105 int CurAutoResponders;
106 int CurMailingLists;
107 uid_t Uid;
108 gid_t Gid;
109 char RealDir[156];
110 char Lang[40];
111
qmailadmin_suid(gid_t Gid,uid_t Uid)112 void qmailadmin_suid (gid_t Gid, uid_t Uid)
113 {
114 if ( geteuid() == 0 ) {
115 if ( setgid(Gid) != 0 ) {
116 printf ("%s", html_text[318]);
117 perror("setgid");
118 vclose();
119 exit (EXIT_FAILURE);
120 }
121 if ( setuid(Uid) != 0 ) {
122 printf ("%s", html_text[319]);
123 perror("setuid");
124 vclose();
125 exit (EXIT_FAILURE);
126 }
127 }
128 }
129
main(argc,argv)130 int main(argc,argv)
131 int argc;
132 char *argv[];
133 {
134 const char *ip_addr=getenv("REMOTE_ADDR");
135 const char *x_forward=getenv("HTTP_X_FORWARDED_FOR");
136 char *pi;
137 char *rm;
138 char returnhttp[MAX_BUFF];
139 char returntext[MAX_BUFF];
140 struct vqpasswd *pw;
141
142 init_globals();
143
144 if (x_forward) ip_addr = x_forward;
145 if (!ip_addr) ip_addr = "127.0.0.1";
146 pi=getenv("PATH_INFO");
147 if ( pi ) pi = strdup(pi);
148
149 if (pi) snprintf (TmpBuf2, sizeof(TmpBuf2), "%s", pi + 5);
150 rm = getenv("REQUEST_METHOD");
151 rm = (rm == NULL ? "" : strdup(rm));
152
153 if ( strncmp(rm , "POST", 4) == 0 ) {
154 get_cgi();
155 } else {
156 TmpCGI = getenv("QUERY_STRING");
157 TmpCGI = (TmpCGI == NULL ? "" : strdup(TmpCGI));
158 }
159
160 if (pi && strncmp(pi, "/com/", 5) == 0) {
161 GetValue(TmpCGI, Username, "user=", sizeof(Username));
162 GetValue(TmpCGI, Domain, "dom=", sizeof(Domain));
163 GetValue(TmpCGI, Time, "time=", sizeof(Time));
164 Mytime = atoi(Time);
165 pw = vauth_getpw( Username, Domain );
166
167 /* get the real uid and gid and change to that user */
168 vget_assign(Domain,RealDir,sizeof(RealDir),&Uid,&Gid);
169 qmailadmin_suid (Gid, Uid);
170
171 if ( chdir(RealDir) < 0 ) {
172 fprintf(stderr, "<h2>%s %s</h2>\n", html_text[171], RealDir );
173 }
174 load_limits();
175
176 set_admin_type();
177
178 if ( AdminType == USER_ADMIN || AdminType == DOMAIN_ADMIN ) {
179 auth_user_domain(ip_addr, pw);
180 } else {
181 auth_system(ip_addr, pw);
182 }
183
184 process_commands();
185
186 } else if (pi && strncmp(pi, "/passwd/", 7) == 0) {
187 char User[MAX_BUFF];
188 char *dom;
189
190 GetValue(TmpCGI, Username, "address=", sizeof(Username));
191 GetValue(TmpCGI, Password, "oldpass=", sizeof(Password));
192 GetValue(TmpCGI, Password1, "newpass1=", sizeof(Password1));
193 GetValue(TmpCGI, Password2, "newpass2=", sizeof(Password2));
194
195 if (*Username && (*Password == '\0') && (*Password1 || *Password2)) {
196 /* username entered, but no password */
197 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[198]);
198 } else if (*Username && *Password) {
199 /* attempt to authenticate user */
200 vget_assign (Domain, RealDir, sizeof(RealDir), &Uid, &Gid);
201 qmailadmin_suid (Gid, Uid);
202
203 strcpy (User, Username);
204 if ((dom = strchr (User, '@')) != NULL) {
205 strcpy (Domain, dom+1);
206 *dom = '\0';
207 }
208
209 if ( *Domain == '\0' ) {
210 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[198]);
211 } else {
212 chdir(RealDir);
213 load_limits();
214
215 pw = vauth_user( User, Domain, Password, "" );
216 if ( pw == NULL ) {
217 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[198]);
218 } else if (pw->pw_flags & NO_PASSWD_CHNG) {
219 strcpy (StatusMessage, "You don't have permission to change your password.");
220 } else if (strcmp (Password1, Password2) != 0) {
221 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[200]);
222 } else if (*Password1 == '\0') {
223 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[234]);
224 } else if (vpasswd (User, Domain, Password1, USE_POP) != VA_SUCCESS) {
225 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[140]);
226 #ifndef TRIVIAL_PASSWORD_ENABLED
227 } else if ( strstr(User,Password1)!=NULL) {
228 snprintf (StatusMessage, sizeof(StatusMessage), "%s\n", html_text[320]);
229 #endif
230 } else {
231 /* success */
232 snprintf (StatusMessage, sizeof(StatusMessage), "%s", html_text[139]);
233 *Password = '\0';
234 send_template ("change_password_success.html");
235
236 return 0;
237 }
238 }
239 }
240
241 send_template ("change_password.html");
242 return 0;
243
244 } else if (*rm) {
245 FILE *fs;
246 char *dom;
247
248 GetValue(TmpCGI, Username, "username=", sizeof(Username));
249 GetValue(TmpCGI, Domain, "domain=", sizeof(Domain));
250 GetValue(TmpCGI, Password, "password=", sizeof(Password));
251 if ((dom = strchr (Username, '@')) != NULL) {
252 strcpy (Domain, dom+1);
253 *dom = '\0';
254 }
255
256 vget_assign(Domain,RealDir,sizeof(RealDir),&Uid,&Gid);
257 qmailadmin_suid (Gid, Uid);
258
259 /* Authenticate a user and domain admin */
260 if ( strlen(Domain) > 0 ) {
261 chdir(RealDir);
262 load_limits();
263
264 pw = vauth_user( Username, Domain, Password, "" );
265 if ( pw == NULL ) {
266 snprintf (StatusMessage, sizeof(StatusMessage), "%s\n", html_text[198]);
267 show_login();
268 vclose();
269 exit(0);
270 }
271
272 snprintf (TmpBuf, sizeof(TmpBuf), "%s/" MAILDIR, pw->pw_dir);
273 del_id_files( TmpBuf);
274
275 Mytime = time(NULL);
276 snprintf (TmpBuf, sizeof(TmpBuf), "%s/" MAILDIR "/%u.qw", pw->pw_dir, (unsigned int) Mytime);
277 fs = fopen(TmpBuf, "w");
278 if ( fs == NULL ) {
279 printf ("%s %s<br>\n", html_text[144], TmpBuf);
280 vclose();
281 exit(0);
282 }
283 memset(TmpBuf, 0, sizeof(TmpBuf));
284 /* set session vars */
285 GetValue(TmpCGI, returntext, "returntext=", sizeof(returntext));
286 GetValue(TmpCGI, returnhttp, "returnhttp=", sizeof(returnhttp));
287 snprinth (TmpBuf, sizeof(TmpBuf), "ip_addr=%C&returntext=%C&returnhttp=%C\n",
288 ip_addr, returntext, returnhttp);
289 fputs(TmpBuf,fs);
290 // fputs(ip_addr, fs);
291 fclose(fs);
292 vget_assign(Domain, TmpBuf1, sizeof(TmpBuf1), &Uid, &Gid);
293 set_admin_type();
294
295 /* show the main menu for domain admins, modify user page
296 for regular users */
297 if (AdminType == DOMAIN_ADMIN) {
298 show_menu(Username, Domain, Mytime);
299 } else {
300 strcpy (ActionUser, Username);
301 moduser();
302 }
303 vclose();
304 exit(0);
305 }
306 }
307 show_login();
308 vclose();
309
310 return 0;
311 }
312
load_lang(char * lang)313 void load_lang (char *lang)
314 {
315 long lang_size;
316 size_t bytes_read;
317 char *lang_entries;
318 char *id;
319 char *p;
320
321 if (open_lang( lang))
322 {
323 // Rare error likely caused by improper installation, should probably be
324 // handled by regular error system, but this is a quick band-aid.
325 printf("Content-Type: text/html\r\n\r\n");
326 printf("<html> <head>\r\n");
327 printf("<title>Failed to open lang file:%s</title>\r\n",lang);
328 printf("</head>\r\n<body>\r\n");
329 printf("<h1>qmailadmin error</h1>\r\n");
330 printf("<p>Failed to open lang file: %s. Please check your lang directory.\r\n", lang);
331 printf("</body></html>\r\n");
332 exit(-1);
333 }
334
335 fseek (lang_fs, 0, SEEK_END);
336 lang_size = ftell (lang_fs);
337 lang_entries = malloc (lang_size);
338
339 if (lang_entries == NULL) return;
340 rewind (lang_fs);
341
342 bytes_read = fread (lang_entries, 1, lang_size, lang_fs);
343 /* error handling for incomplete reads? */
344
345 id = strtok (lang_entries, " \t");
346 while (id) {
347 p = strtok (NULL, "\n");
348 if (p == NULL) break;
349 html_text[atoi(id)] = p;
350 id = strtok (NULL, " \t");
351 }
352
353 /* Do not free lang_entries! html_text points into it! */
354 }
355
init_globals()356 void init_globals()
357 {
358 char *accept_lang;
359 char *langptr, *qptr;
360 char *charset;
361 int lang_err;
362 int i;
363 float maxq, thisq;
364
365 memset(CGIValues, 0, sizeof(CGIValues));
366 CGIValues['0'] = 0;
367 CGIValues['1'] = 1;
368 CGIValues['2'] = 2;
369 CGIValues['3'] = 3;
370 CGIValues['4'] = 4;
371 CGIValues['5'] = 5;
372 CGIValues['6'] = 6;
373 CGIValues['7'] = 7;
374 CGIValues['8'] = 8;
375 CGIValues['9'] = 9;
376 CGIValues['A'] = 10;
377 CGIValues['B'] = 11;
378 CGIValues['C'] = 12;
379 CGIValues['D'] = 13;
380 CGIValues['E'] = 14;
381 CGIValues['F'] = 15;
382 CGIValues['a'] = 10;
383 CGIValues['b'] = 11;
384 CGIValues['c'] = 12;
385 CGIValues['d'] = 13;
386 CGIValues['e'] = 14;
387 CGIValues['f'] = 15;
388
389 actout = stdout;
390 memset(Username, 0, sizeof(Username));
391 memset(Domain, 0, sizeof(Domain));
392 memset(Password, 0, sizeof(Password));
393 memset(Quota, 0, sizeof(Quota));
394 memset(Time, 0, sizeof(Time));
395 memset(ActionUser, 0, sizeof(ActionUser));
396 memset(Newu, 0, sizeof(Newu));
397 memset(Password1, 0, sizeof(Password1));
398 memset(Password2, 0, sizeof(Password2));
399 memset(Crypted, 0, sizeof(Crypted));
400 memset(Alias, 0, sizeof(Alias));
401 memset(Message, 0, sizeof(Message));
402 memset(TmpBuf, 0, sizeof(TmpBuf));
403 memset(TmpBuf1, 0, sizeof(TmpBuf1));
404 memset(TmpBuf2, 0, sizeof(TmpBuf2));
405 memset(TmpBuf3, 0, sizeof(TmpBuf3));
406
407 AdminType = NO_ADMIN;
408
409 lang_fs = NULL;
410
411 /* Parse HTTP_ACCEPT_LANGUAGE to find highest preferred language
412 * that we have a translation for. Example setting:
413 * de-de, ja;q=0.25, en;q=0.50, de;q=0.75
414 * The q= lines determine which is most preferred, defaults to 1.
415 * Our routine starts with en at 0.0, and then would try de-de (1.00),
416 * de (1.00), ja (0.25), en (0.50), and then de (0.75).
417 */
418
419 /* default to English at 0.00 preference */
420 maxq = 0.0;
421 strcpy (Lang, "en");
422
423 /* read in preferred languages */
424 langptr = getenv("HTTP_ACCEPT_LANGUAGE");
425 if (langptr != NULL) {
426 accept_lang = malloc (strlen(langptr));
427 strcpy (accept_lang, langptr);
428 langptr = strtok(accept_lang, " ,\n");
429 while (langptr != NULL) {
430 qptr = strstr (langptr, ";q=");
431 if (qptr != NULL) {
432 *qptr = '\0'; /* convert semicolon to NULL */
433 thisq = (float) atof (qptr+3);
434 } else {
435 thisq = 1.0;
436 }
437
438 /* if this is a better match than our previous best, try it */
439 if (thisq > maxq) {
440 lang_err = open_lang (langptr);
441
442 /* Remove this next section for strict interpretation of
443 * HTTP_ACCEPT_LANGUAGE. It will try language xx (with the
444 * same q value) if xx-yy fails.
445 */
446 if ((lang_err == -1) && (langptr[2] == '-')) {
447 langptr[2] = '\0';
448 lang_err = open_lang (langptr);
449 }
450
451 if (lang_err == 0) {
452 maxq = thisq;
453 strcpy (Lang, langptr);
454 }
455 }
456 langptr = strtok (NULL, " ,\n");
457 }
458
459 free(accept_lang);
460 }
461
462 /* best language choice is now in 'Lang' */
463
464 /* build table of html_text entries */
465 for (i = 0; i <= MAX_LANG_STR; i++) html_text[i] = "";
466
467 /* read English first as defaults for incomplete language files */
468 if (strcmp (Lang, "en") != 0) load_lang ("en");
469
470 /* load the preferred language */
471 load_lang (Lang);
472
473 /* open the color table */
474 open_colortable();
475
476 umask(VPOPMAIL_UMASK);
477
478 charset = html_text[0];
479 printf ("Content-Type: text/html; charset=%s\n",
480 *charset == '\0' ? "iso-8859-1" : charset);
481 #ifdef NO_CACHE
482 printf ("Cache-Control: no-cache\n");
483 printf ("Cache-Control: no-store\n");
484 printf ("Pragma: no-cache\n");
485 printf ("Expires: Thu, 01 Dec 1994 16:00:00 GMT\n");
486 #endif
487 printf ("\n");
488 }
489
del_id_files(char * dirname)490 void del_id_files( char *dirname )
491 {
492 DIR *mydir;
493 struct dirent *mydirent;
494
495 mydir = opendir(dirname);
496 if ( mydir == NULL ) return;
497
498 while((mydirent=readdir(mydir))!=NULL){
499 if ( strstr(mydirent->d_name,".qw")!=0 ) {
500 snprintf (TmpBuf3, sizeof(TmpBuf3), "%s/%s", dirname, mydirent->d_name);
501 unlink(TmpBuf3);
502 }
503 }
504 closedir(mydir);
505 }
506
quickAction(char * username,int action)507 void quickAction (char *username, int action)
508 {
509 /* This feature sponsored by PinkRoccade Public Sector, Sept 2004 */
510
511 struct stat fileinfo;
512 char dotqmailfn[MAX_BUFF];
513 char *space, *ar, *ez;
514 char *aliasline;
515
516 /* Note that all of the functions called from quickAction() assume
517 * that the username to modify is in a global called "ActionUser"
518 * It would be better to pass this information as a parameter, but
519 * that's how it was originally done. The code in command.c that
520 * calls quickAction() passes ActionUser as the username parameter
521 * in hopes that someday we'll remove the globals and pass parameters.
522 */
523
524 /* first check for alias/forward, autorepsonder (or even mailing list) */
525 aliasline = valias_select (username, Domain);
526 if (aliasline != NULL) {
527 /* Autoresponder/Mailing List detection algorithm:
528 * We're looking for either '/autorespond ' or '/ezmlm-reject ' to
529 * appear in the first line, before a space appears
530 */
531 space = strstr (aliasline, " ");
532 ar = strstr (aliasline, "/autorespond ");
533 ez = strstr (aliasline, "/ezmlm-reject ");
534 if (ar && space && (ar < space)) {
535 /* autorepsonder */
536 if (action == ACTION_MODIFY) modautorespond();
537 else if (action == ACTION_DELETE) delautorespond();
538 } else if (ez && space && (ez < space)) {
539 /* mailing list (cdb-backend only) */
540 if (action == ACTION_MODIFY) modmailinglist();
541 else if (action == ACTION_DELETE) delmailinglist();
542 } else {
543 /* it's just a forward/alias of some sort */
544 if (action == ACTION_MODIFY) moddotqmail();
545 else if (action == ACTION_DELETE) deldotqmail();
546 }
547 } else if (vauth_getpw (username, Domain)) {
548 /* POP/IMAP account */
549 if (action == ACTION_MODIFY) moduser();
550 else if (action == ACTION_DELETE) {
551 // don't allow deletion of postmaster account
552 if (strcasecmp (username, "postmaster") == 0) {
553 snprinth (StatusMessage, sizeof(StatusMessage), "%s", html_text[317]);
554 show_menu(Username, Domain, Mytime);
555 vclose();
556 } else deluser();
557 }
558 } else {
559 /* check for mailing list on SQL backend (not in valias_select) */
560 snprintf (dotqmailfn, sizeof(dotqmailfn), ".qmail-%s", username);
561 str_replace (dotqmailfn+7, '.', ':');
562 if (stat (dotqmailfn, &fileinfo) == 0) {
563 /* mailing list (MySQL backend) */
564 if (action == ACTION_MODIFY) modmailinglist();
565 else if (action == ACTION_DELETE) delmailinglist();
566 } else {
567 /* user does not exist */
568 snprinth (StatusMessage, sizeof(StatusMessage), "%s (%H@%H)",
569 html_text[153], username, Domain);
570 show_menu(Username, Domain, Mytime);
571 vclose();
572 }
573 }
574 }
575