1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include "common.h"
4 
5 #include <sys/stat.h>
6 #include <dirent.h>
7 
8 #include "utils.h"
9 
10 #include "seafile-session.h"
11 #include "seafile-error.h"
12 #include "user-mgr.h"
13 #include "seaf-db.h"
14 #include "seaf-utils.h"
15 
16 #include <openssl/sha.h>
17 #include <openssl/rand.h>
18 #include <openssl/evp.h>
19 
20 #ifdef HAVE_LDAP
21   #ifndef WIN32
22     #define LDAP_DEPRECATED 1
23     #include <ldap.h>
24   #else
25     #include <winldap.h>
26     #include <winber.h>
27     #ifndef LDAP_OPT_SUCCESS
28     #define LDAP_OPT_SUCCESS LDAP_SUCCESS
29     #endif
30   #endif
31 #endif
32 
33 #define DEBUG_FLAG  CCNET_DEBUG_PEER
34 #include "log.h"
35 
36 #define DEFAULT_SAVING_INTERVAL_MSEC 30000
37 
38 #define DEFAULT_MAX_CONNECTIONS 100
39 
40 G_DEFINE_TYPE (CcnetUserManager, ccnet_user_manager, G_TYPE_OBJECT);
41 
42 
43 #define GET_PRIV(o)  \
44    (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_USER_MANAGER, CcnetUserManagerPriv))
45 
46 
47 static int open_db (CcnetUserManager *manager);
48 
49 #ifdef HAVE_LDAP
50 static int try_load_ldap_settings (CcnetUserManager *manager);
51 #endif
52 
53 struct CcnetUserManagerPriv {
54     CcnetDB    *db;
55     int         max_users;
56 };
57 
58 static void
ccnet_user_manager_class_init(CcnetUserManagerClass * klass)59 ccnet_user_manager_class_init (CcnetUserManagerClass *klass)
60 {
61 
62     g_type_class_add_private (klass, sizeof (CcnetUserManagerPriv));
63 }
64 
65 static void
ccnet_user_manager_init(CcnetUserManager * manager)66 ccnet_user_manager_init (CcnetUserManager *manager)
67 {
68     manager->priv = GET_PRIV(manager);
69 }
70 
71 CcnetUserManager*
ccnet_user_manager_new(SeafileSession * session)72 ccnet_user_manager_new (SeafileSession *session)
73 {
74     CcnetUserManager* manager;
75 
76     manager = g_object_new (CCNET_TYPE_USER_MANAGER, NULL);
77     manager->session = session;
78     manager->user_hash = g_hash_table_new (g_str_hash, g_str_equal);
79 
80     return manager;
81 }
82 
83 #define DEFAULT_PASSWD_HASH_ITER 10000
84 
85 // return current active user number
86 static int
get_current_user_number(CcnetUserManager * manager)87 get_current_user_number (CcnetUserManager *manager)
88 {
89     int total = 0, count;
90 
91     count = ccnet_user_manager_count_emailusers (manager, "DB");
92     if (count < 0) {
93         ccnet_warning ("Failed to get user number from DB.\n");
94         return -1;
95     }
96     total += count;
97 
98 #ifdef HAVE_LDAP
99     if (manager->use_ldap) {
100         count = ccnet_user_manager_count_emailusers (manager, "LDAP");
101         if (count < 0) {
102             ccnet_warning ("Failed to get user number from LDAP.\n");
103             return -1;
104         }
105         total += count;
106     }
107 #endif
108 
109     return total;
110 }
111 
112 static gboolean
check_user_number(CcnetUserManager * manager,gboolean allow_equal)113 check_user_number (CcnetUserManager *manager, gboolean allow_equal)
114 {
115     if (manager->priv->max_users == 0) {
116         return TRUE;
117     }
118 
119     int cur_num = get_current_user_number (manager);
120     if (cur_num < 0) {
121         return FALSE;
122     }
123 
124     if ((allow_equal && cur_num > manager->priv->max_users) ||
125         (!allow_equal && cur_num >= manager->priv->max_users)) {
126         ccnet_warning ("The number of users exceeds limit, max %d, current %d\n",
127                        manager->priv->max_users, cur_num);
128         return FALSE;
129     }
130 
131     return TRUE;
132 }
133 
134 int
ccnet_user_manager_prepare(CcnetUserManager * manager)135 ccnet_user_manager_prepare (CcnetUserManager *manager)
136 {
137     int ret;
138 
139 #ifdef HAVE_LDAP
140     if (try_load_ldap_settings (manager) < 0)
141         return -1;
142 #endif
143 
144     int iter = g_key_file_get_integer (manager->session->ccnet_config,
145                                        "USER", "PASSWORD_HASH_ITERATIONS",
146                                        NULL);
147     if (iter <= 0)
148         iter = DEFAULT_PASSWD_HASH_ITER;
149     manager->passwd_hash_iter = iter;
150 
151     manager->userdb_path = g_build_filename (manager->session->ccnet_dir,
152                                              "user-db", NULL);
153     ret = open_db(manager);
154     if (ret < 0)
155         return ret;
156 
157     if (!check_user_number (manager, TRUE)) {
158         return -1;
159     }
160 
161     return 0;
162 }
163 
164 void
ccnet_user_manager_free(CcnetUserManager * manager)165 ccnet_user_manager_free (CcnetUserManager *manager)
166 {
167     g_object_unref (manager);
168 }
169 
170 void
ccnet_user_manager_start(CcnetUserManager * manager)171 ccnet_user_manager_start (CcnetUserManager *manager)
172 {
173 
174 }
175 
ccnet_user_manager_on_exit(CcnetUserManager * manager)176 void ccnet_user_manager_on_exit (CcnetUserManager *manager)
177 {
178 }
179 
180 void
ccnet_user_manager_set_max_users(CcnetUserManager * manager,gint64 max_users)181 ccnet_user_manager_set_max_users (CcnetUserManager *manager, gint64 max_users)
182 {
183     manager->priv->max_users = max_users;
184 }
185 
186 /* -------- LDAP related --------- */
187 
188 #ifdef HAVE_LDAP
189 
190 
try_load_ldap_settings(CcnetUserManager * manager)191 static int try_load_ldap_settings (CcnetUserManager *manager)
192 {
193     GKeyFile *config = manager->session->ccnet_config;
194 
195     manager->ldap_host = ccnet_key_file_get_string (config, "LDAP", "HOST");
196     if (!manager->ldap_host)
197         return 0;
198 
199     manager->use_ldap = TRUE;
200 
201 #ifdef WIN32
202     manager->use_ssl = g_key_file_get_boolean (config, "LDAP", "USE_SSL", NULL);
203 #endif
204 
205     char *base_list = ccnet_key_file_get_string (config, "LDAP", "BASE");
206     if (!base_list) {
207         ccnet_warning ("LDAP: BASE not found in config file.\n");
208         return -1;
209     }
210     manager->base_list = g_strsplit (base_list, ";", -1);
211 
212     manager->filter = ccnet_key_file_get_string (config, "LDAP", "FILTER");
213 
214     manager->user_dn = ccnet_key_file_get_string (config, "LDAP", "USER_DN");
215     if (manager->user_dn) {
216         manager->password = ccnet_key_file_get_string (config, "LDAP", "PASSWORD");
217         if (!manager->password) {
218             ccnet_warning ("LDAP: PASSWORD not found in config file.\n");
219             return -1;
220         }
221     }
222     /* Use anonymous if user_dn is not set. */
223 
224     manager->login_attr = ccnet_key_file_get_string (config, "LDAP", "LOGIN_ATTR");
225     if (!manager->login_attr)
226         manager->login_attr = g_strdup("mail");
227 
228     GError *error = NULL;
229     manager->follow_referrals = g_key_file_get_boolean (config,
230                                                         "LDAP", "FOLLOW_REFERRALS",
231                                                         &error);
232     if (error) {
233         /* Default is follow referrals. */
234         g_clear_error (&error);
235         manager->follow_referrals = TRUE;
236     }
237 
238     return 0;
239 }
240 
ldap_init_and_bind(CcnetUserManager * manager,const char * host,gboolean use_ssl,const char * user_dn,const char * password)241 static LDAP *ldap_init_and_bind (CcnetUserManager *manager,
242                                  const char *host,
243 #ifdef WIN32
244                                  gboolean use_ssl,
245 #endif
246                                  const char *user_dn,
247                                  const char *password)
248 {
249     LDAP *ld;
250     int res;
251     int desired_version = LDAP_VERSION3;
252 
253 #ifndef WIN32
254     res = ldap_initialize (&ld, host);
255     if (res != LDAP_SUCCESS) {
256         ccnet_warning ("ldap_initialize failed: %s.\n", ldap_err2string(res));
257         return NULL;
258     }
259 #else
260     char *host_copy = g_strdup (host);
261     if (!use_ssl)
262         ld = ldap_init (host_copy, LDAP_PORT);
263     else
264         ld = ldap_sslinit (host_copy, LDAP_SSL_PORT, 1);
265     g_free (host_copy);
266     if (!ld) {
267         ccnet_warning ("ldap_init failed: %ul.\n", LdapGetLastError());
268         return NULL;
269     }
270 #endif
271 
272     /* set the LDAP version to be 3 */
273     res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version);
274     if (res != LDAP_OPT_SUCCESS) {
275         ccnet_warning ("ldap_set_option failed: %s.\n", ldap_err2string(res));
276         return NULL;
277     }
278 
279     res = ldap_set_option (ld, LDAP_OPT_REFERRALS,
280                            manager->follow_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
281     if (res != LDAP_OPT_SUCCESS) {
282         ccnet_warning ("ldap_set_option referrals failed: %s.\n",
283                        ldap_err2string(res));
284         return NULL;
285     }
286 
287     if (user_dn) {
288 #ifndef WIN32
289         res = ldap_bind_s (ld, user_dn, password, LDAP_AUTH_SIMPLE);
290 #else
291         char *dn_copy = g_strdup(user_dn);
292         char *password_copy = g_strdup(password);
293         res = ldap_bind_s (ld, dn_copy, password_copy, LDAP_AUTH_SIMPLE);
294         g_free (dn_copy);
295         g_free (password_copy);
296 #endif
297         if (res != LDAP_SUCCESS ) {
298             ccnet_warning ("ldap_bind failed for user %s: %s.\n",
299                            user_dn, ldap_err2string(res));
300             ldap_unbind_s (ld);
301             return NULL;
302         }
303     }
304 
305     return ld;
306 }
307 
308 static gboolean
get_uid_cb(CcnetDBRow * row,void * data)309 get_uid_cb (CcnetDBRow *row, void *data)
310 {
311     int *id = data;
312     *id = seaf_db_row_get_column_int (row, 0);
313     return FALSE;
314 }
315 
316 static int
add_ldapuser(CcnetDB * db,const char * email,const char * password,gboolean is_staff,gboolean is_active,const char * extra_attrs)317 add_ldapuser (CcnetDB *db,
318               const char *email,
319               const char *password,
320               gboolean is_staff,
321               gboolean is_active,
322               const char *extra_attrs)
323 {
324     int rc;
325     int uid = -1;
326 
327     rc = seaf_db_statement_foreach_row (db,
328                                          "SELECT id FROM LDAPUsers WHERE email = ?",
329                                          get_uid_cb, &uid, 1, "string", email);
330 
331     if (rc < 0) {
332         return rc;
333     }
334 
335     if (rc == 1) {
336         return uid;
337     }
338 
339     if (extra_attrs)
340         rc = seaf_db_statement_query (db,
341                                        "INSERT INTO LDAPUsers (email, password, is_staff, "
342                                        "is_active, extra_attrs) VALUES (?, ?, ?, ?, ?)",
343                                        5, "string", email, "string", password, "int",
344                                        is_staff, "int", is_active, "string", extra_attrs);
345     else
346         rc = seaf_db_statement_query (db,
347                                        "INSERT INTO LDAPUsers (email, password, is_staff, "
348                                        "is_active) VALUES (?, ?, ?, ?)", 4, "string", email,
349                                        "string", password, "int", is_staff, "int", is_active);
350     if (rc < 0) {
351         return rc;
352     }
353 
354     seaf_db_statement_foreach_row (db,
355                                     "SELECT id FROM LDAPUsers WHERE email = ?",
356                                     get_uid_cb, &uid, 1, "string", email);
357 
358     return uid;
359 }
360 
ldap_verify_user_password(CcnetUserManager * manager,const char * uid,const char * password)361 static int ldap_verify_user_password (CcnetUserManager *manager,
362                                       const char *uid,
363                                       const char *password)
364 {
365     LDAP *ld = NULL;
366     int res;
367     GString *filter;
368     char *filter_str = NULL;
369     char *attrs[2];
370     LDAPMessage *msg = NULL, *entry;
371     char *dn = NULL;
372     int ret = 0;
373 
374     /* First search for the DN with the given uid. */
375 
376     ld = ldap_init_and_bind (manager,
377                              manager->ldap_host,
378 #ifdef WIN32
379                              manager->use_ssl,
380 #endif
381                              manager->user_dn,
382                              manager->password);
383     if (!ld) {
384         ccnet_warning ("Please check USER_DN and PASSWORD settings.\n");
385         return -1;
386     }
387 
388     filter = g_string_new (NULL);
389     if (!manager->filter)
390         g_string_printf (filter, "(%s=%s)", manager->login_attr, uid);
391     else
392         g_string_printf (filter, "(&(%s=%s) (%s))",
393                          manager->login_attr, uid, manager->filter);
394     filter_str = g_string_free (filter, FALSE);
395 
396     attrs[0] = manager->login_attr;
397     attrs[1] = NULL;
398 
399     char **base;
400     for (base = manager->base_list; *base; base++) {
401         res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE,
402                              filter_str, attrs, 0, &msg);
403         if (res != LDAP_SUCCESS) {
404             ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n",
405                            manager->login_attr, uid, *base, ldap_err2string(res));
406             ccnet_warning ("Please check BASE setting in ccnet.conf.\n");
407             ret = -1;
408             ldap_msgfree (msg);
409             goto out;
410         }
411 
412         entry = ldap_first_entry (ld, msg);
413         if (entry) {
414             dn = ldap_get_dn (ld, entry);
415             ldap_msgfree (msg);
416             break;
417         }
418 
419         ldap_msgfree (msg);
420     }
421 
422     if (!dn) {
423         ccnet_debug ("Cannot find user %s in LDAP.\n", uid);
424         ret = -1;
425         goto out;
426     }
427 
428     /* Then bind the DN with password. */
429 
430     ldap_unbind_s (ld);
431 
432     ld = ldap_init_and_bind (manager,
433                              manager->ldap_host,
434 #ifdef WIN32
435                              manager->use_ssl,
436 #endif
437                              dn, password);
438     if (!ld) {
439         ccnet_debug ("Password incorrect for %s in LDAP.\n", uid);
440         ret = -1;
441     }
442 
443 out:
444     ldap_memfree (dn);
445     g_free (filter_str);
446     if (ld) ldap_unbind_s (ld);
447     return ret;
448 }
449 
450 /*
451  * @uid: user's uid, list all users if * is passed in.
452  */
ldap_list_users(CcnetUserManager * manager,const char * uid,int start,int limit)453 static GList *ldap_list_users (CcnetUserManager *manager, const char *uid,
454                                int start, int limit)
455 {
456     LDAP *ld = NULL;
457     GList *ret = NULL;
458     int res;
459     GString *filter;
460     char *filter_str;
461     char *attrs[2];
462     LDAPMessage *msg = NULL, *entry;
463 
464     ld = ldap_init_and_bind (manager,
465                              manager->ldap_host,
466 #ifdef WIN32
467                              manager->use_ssl,
468 #endif
469                              manager->user_dn,
470                              manager->password);
471     if (!ld) {
472         ccnet_warning ("Please check USER_DN and PASSWORD settings.\n");
473         return NULL;
474     }
475 
476     filter = g_string_new (NULL);
477     if (!manager->filter)
478         g_string_printf (filter, "(%s=%s)", manager->login_attr, uid);
479     else
480         g_string_printf (filter, "(&(%s=%s) (%s))",
481                          manager->login_attr, uid, manager->filter);
482     filter_str = g_string_free (filter, FALSE);
483 
484     attrs[0] = manager->login_attr;
485     attrs[1] = NULL;
486 
487     int i = 0;
488     if (start == -1)
489         start = 0;
490 
491     char **base;
492     for (base = manager->base_list; *base; ++base) {
493         res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE,
494                              filter_str, attrs, 0, &msg);
495         if (res != LDAP_SUCCESS) {
496             ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n",
497                            manager->login_attr, uid, *base, ldap_err2string(res));
498             ccnet_warning ("Please check BASE setting in ccnet.conf.\n");
499             ret = NULL;
500             ldap_msgfree (msg);
501             goto out;
502         }
503 
504         for (entry = ldap_first_entry (ld, msg);
505              entry != NULL;
506              entry = ldap_next_entry (ld, entry), ++i) {
507             char *attr;
508             char **vals;
509             BerElement *ber;
510             CcnetEmailUser *user;
511 
512             if (i < start)
513                 continue;
514             if (limit >= 0 && i >= start + limit) {
515                 ldap_msgfree (msg);
516                 goto out;
517             }
518 
519             attr = ldap_first_attribute (ld, entry, &ber);
520             vals = ldap_get_values (ld, entry, attr);
521 
522             char *email_l = g_ascii_strdown (vals[0], -1);
523             user = g_object_new (CCNET_TYPE_EMAIL_USER,
524                                  "id", 0,
525                                  "email", email_l,
526                                  "is_staff", FALSE,
527                                  "is_active", TRUE,
528                                  "ctime", (gint64)0,
529                                  "source", "LDAP",
530                                  "password", "!",
531                                  NULL);
532             g_free (email_l);
533             ret = g_list_prepend (ret, user);
534 
535             ldap_memfree (attr);
536             ldap_value_free (vals);
537             ber_free (ber, 0);
538         }
539 
540         ldap_msgfree (msg);
541     }
542 
543 out:
544     g_free (filter_str);
545     if (ld) ldap_unbind_s (ld);
546     return ret;
547 }
548 
549 #endif  /* HAVE_LDAP */
550 
551 /* -------- DB Operations -------- */
552 
check_db_table(SeafDB * db)553 static int check_db_table (SeafDB *db)
554 {
555     char *sql;
556 
557     int db_type = seaf_db_type (db);
558     if (db_type == SEAF_DB_TYPE_MYSQL) {
559         sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
560             "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
561             "email VARCHAR(255), passwd VARCHAR(256), "
562             "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, "
563             "ctime BIGINT, reference_id VARCHAR(255),"
564             "UNIQUE INDEX (email), UNIQUE INDEX (reference_id))"
565             "ENGINE=INNODB";
566         if (seaf_db_query (db, sql) < 0)
567             return -1;
568         sql = "CREATE TABLE IF NOT EXISTS Binding (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
569             "email VARCHAR(255), peer_id CHAR(41),"
570             "UNIQUE INDEX (peer_id), INDEX (email(20)))"
571             "ENGINE=INNODB";
572         if (seaf_db_query (db, sql) < 0)
573             return -1;
574 
575         sql = "CREATE TABLE IF NOT EXISTS UserRole ("
576           "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
577           "email VARCHAR(255), role VARCHAR(255), UNIQUE INDEX (email)) "
578           "ENGINE=INNODB";
579         if (seaf_db_query (db, sql) < 0)
580             return -1;
581 
582         sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
583           "id BIGINT PRIMARY KEY AUTO_INCREMENT, "
584           "email VARCHAR(255) NOT NULL, password varchar(255) NOT NULL, "
585           "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT, "
586           "reference_id VARCHAR(255), "
587           "UNIQUE INDEX(email), UNIQUE INDEX (reference_id)) ENGINE=INNODB";
588         if (seaf_db_query (db, sql) < 0)
589             return -1;
590 
591         sql = "CREATE TABLE IF NOT EXISTS LDAPConfig ( "
592           "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, cfg_group VARCHAR(255) NOT NULL,"
593           "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER) ENGINE=INNODB";
594         if (seaf_db_query (db, sql) < 0)
595             return -1;
596 
597     } else if (db_type == SEAF_DB_TYPE_SQLITE) {
598         sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
599             "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
600             "email TEXT, passwd TEXT, is_staff bool NOT NULL, "
601             "is_active bool NOT NULL, ctime INTEGER, "
602             "reference_id TEXT)";
603         if (seaf_db_query (db, sql) < 0)
604             return -1;
605 
606         sql = "CREATE UNIQUE INDEX IF NOT EXISTS email_index on EmailUser (email)";
607         if (seaf_db_query (db, sql) < 0)
608             return -1;
609 
610         sql = "CREATE UNIQUE INDEX IF NOT EXISTS reference_id_index on EmailUser (reference_id)";
611         if (seaf_db_query (db, sql) < 0)
612             return -1;
613 
614         sql = "CREATE TABLE IF NOT EXISTS Binding (email TEXT, peer_id TEXT)";
615         if (seaf_db_query (db, sql) < 0)
616             return -1;
617 
618         sql = "CREATE INDEX IF NOT EXISTS email_index on Binding (email)";
619         if (seaf_db_query (db, sql) < 0)
620             return -1;
621 
622         sql = "CREATE UNIQUE INDEX IF NOT EXISTS peer_index on Binding (peer_id)";
623         if (seaf_db_query (db, sql) < 0)
624             return -1;
625 
626         sql = "CREATE TABLE IF NOT EXISTS UserRole (email TEXT, role TEXT)";
627         if (seaf_db_query (db, sql) < 0)
628             return -1;
629 
630         sql = "CREATE INDEX IF NOT EXISTS userrole_email_index on UserRole (email)";
631         if (seaf_db_query (db, sql) < 0)
632             return -1;
633 
634         sql = "CREATE UNIQUE INDEX IF NOT EXISTS userrole_userrole_index on UserRole (email, role)";
635         if (seaf_db_query (db, sql) < 0)
636             return -1;
637 
638         sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
639           "id INTEGER PRIMARY KEY AUTOINCREMENT, "
640           "email TEXT NOT NULL, password TEXT NOT NULL, "
641           "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT, "
642           "reference_id TEXT)";
643         if (seaf_db_query (db, sql) < 0)
644             return -1;
645 
646         sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_email_index on LDAPUsers(email)";
647         if (seaf_db_query (db, sql) < 0)
648             return -1;
649 
650         sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_reference_id_index on LDAPUsers(reference_id)";
651         if (seaf_db_query (db, sql) < 0)
652             return -1;
653 
654         sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (cfg_group VARCHAR(255) NOT NULL,"
655           "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)";
656         if (seaf_db_query (db, sql) < 0)
657             return -1;
658 
659     } else if (db_type == SEAF_DB_TYPE_PGSQL) {
660         sql = "CREATE TABLE IF NOT EXISTS EmailUser ("
661             "id SERIAL PRIMARY KEY, "
662             "email VARCHAR(255), passwd VARCHAR(256), "
663             "is_staff INTEGER NOT NULL, is_active INTEGER NOT NULL, "
664             "ctime BIGINT, reference_id VARCHAR(255), UNIQUE (email))";
665         if (seaf_db_query (db, sql) < 0)
666             return -1;
667 
668         //if (!pgsql_index_exists (db, "emailuser_reference_id_idx")) {
669         //    sql = "CREATE UNIQUE INDEX emailuser_reference_id_idx ON EmailUser (reference_id)";
670         //    if (seaf_db_query (db, sql) < 0)
671         //        return -1;
672         //}
673 
674         sql = "CREATE TABLE IF NOT EXISTS Binding (email VARCHAR(255), peer_id CHAR(41),"
675             "UNIQUE (peer_id))";
676         if (seaf_db_query (db, sql) < 0)
677             return -1;
678 
679         sql = "CREATE TABLE IF NOT EXISTS UserRole (email VARCHAR(255), "
680           " role VARCHAR(255), UNIQUE (email, role))";
681         if (seaf_db_query (db, sql) < 0)
682             return -1;
683 
684         //if (!pgsql_index_exists (db, "userrole_email_idx")) {
685         //    sql = "CREATE INDEX userrole_email_idx ON UserRole (email)";
686         //    if (seaf_db_query (db, sql) < 0)
687         //        return -1;
688         //}
689 
690         sql = "CREATE TABLE IF NOT EXISTS LDAPUsers ("
691           "id SERIAL PRIMARY KEY, "
692           "email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, "
693           "is_staff SMALLINT NOT NULL, is_active SMALLINT NOT NULL, extra_attrs TEXT,"
694           "reference_id VARCHAR(255))";
695         if (seaf_db_query (db, sql) < 0)
696             return -1;
697 
698         //if (!pgsql_index_exists (db, "ldapusers_email_idx")) {
699         //    sql = "CREATE UNIQUE INDEX ldapusers_email_idx ON LDAPUsers (email)";
700         //    if (seaf_db_query (db, sql) < 0)
701         //        return -1;
702         //}
703 
704         //if (!pgsql_index_exists (db, "ldapusers_reference_id_idx")) {
705         //    sql = "CREATE UNIQUE INDEX ldapusers_reference_id_idx ON LDAPUsers (reference_id)";
706         //    if (seaf_db_query (db, sql) < 0)
707         //        return -1;
708         //}
709 
710         sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (cfg_group VARCHAR(255) NOT NULL,"
711           "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)";
712         if (seaf_db_query (db, sql) < 0)
713             return -1;
714     }
715 
716     return 0;
717 }
718 
719 
720 static CcnetDB *
open_sqlite_db(CcnetUserManager * manager)721 open_sqlite_db (CcnetUserManager *manager)
722 {
723     CcnetDB *db = NULL;
724     char *db_dir;
725     char *db_path;
726 
727     db_dir = g_build_filename (manager->session->ccnet_dir, "PeerMgr", NULL);
728     if (checkdir_with_mkdir(db_dir) < 0) {
729         ccnet_error ("Cannot open db dir %s: %s\n", db_dir,
730                      strerror(errno));
731         return NULL;
732     }
733     g_free (db_dir);
734 
735     db_path = g_build_filename (manager->session->ccnet_dir, "PeerMgr",
736                                 "usermgr.db", NULL);
737     db = seaf_db_new_sqlite (db_path, DEFAULT_MAX_CONNECTIONS);
738     g_free (db_path);
739 
740     return db;
741 }
742 
743 static int
open_db(CcnetUserManager * manager)744 open_db (CcnetUserManager *manager)
745 {
746     CcnetDB *db = NULL;
747 
748     switch (seaf_db_type(manager->session->ccnet_db)) {
749     /* To be compatible with the db file layout of 0.9.1 version,
750      * we don't use conf-dir/ccnet.db for user and peer info, but
751      * user conf-dir/PeerMgr/peermgr.db and conf-dir/PeerMgr/usermgr.db instead.
752      */
753     case SEAF_DB_TYPE_SQLITE:
754         db = open_sqlite_db (manager);
755         break;
756     case SEAF_DB_TYPE_PGSQL:
757     case SEAF_DB_TYPE_MYSQL:
758         db = manager->session->ccnet_db;
759         break;
760     }
761 
762     if (!db)
763         return -1;
764 
765     manager->priv->db = db;
766     if ((manager->session->ccnet_create_tables || seaf_db_type(db) == SEAF_DB_TYPE_PGSQL)
767         && check_db_table (db) < 0) {
768         ccnet_warning ("Failed to create user db tables.\n");
769         return -1;
770     }
771     return 0;
772 }
773 
774 
775 /* -------- EmailUser Management -------- */
776 
777 /* This fixed salt is used in very early versions. It's kept for compatibility.
778  * For the current password hashing algorithm, please see hash_password_pbkdf2_sha256()
779  */
780 static unsigned char salt[8] = { 0xdb, 0x91, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 };
781 
782 static void
hash_password(const char * passwd,char * hashed_passwd)783 hash_password (const char *passwd, char *hashed_passwd)
784 {
785     unsigned char sha1[20];
786     SHA_CTX s;
787 
788     SHA1_Init (&s);
789     SHA1_Update (&s, passwd, strlen(passwd));
790     SHA1_Final (sha1, &s);
791     rawdata_to_hex (sha1, hashed_passwd, 20);
792 }
793 
794 static void
hash_password_salted(const char * passwd,char * hashed_passwd)795 hash_password_salted (const char *passwd, char *hashed_passwd)
796 {
797     unsigned char sha[SHA256_DIGEST_LENGTH];
798     SHA256_CTX s;
799 
800     SHA256_Init (&s);
801     SHA256_Update (&s, passwd, strlen(passwd));
802     SHA256_Update (&s, salt, sizeof(salt));
803     SHA256_Final (sha, &s);
804     rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
805 }
806 
807 static void
hash_password_pbkdf2_sha256(const char * passwd,int iterations,char ** db_passwd)808 hash_password_pbkdf2_sha256 (const char *passwd,
809                              int iterations,
810                              char **db_passwd)
811 {
812     guint8 sha[SHA256_DIGEST_LENGTH];
813     guint8 salt[SHA256_DIGEST_LENGTH];
814     char hashed_passwd[SHA256_DIGEST_LENGTH*2+1];
815     char salt_str[SHA256_DIGEST_LENGTH*2+1];
816 
817     if (!RAND_bytes (salt, sizeof(salt))) {
818         ccnet_warning ("Failed to generate salt "
819                        "with RAND_bytes(), use RAND_pseudo_bytes().\n");
820         RAND_pseudo_bytes (salt, sizeof(salt));
821     }
822 
823     PKCS5_PBKDF2_HMAC (passwd, strlen(passwd),
824                        salt, sizeof(salt),
825                        iterations,
826                        EVP_sha256(),
827                        sizeof(sha), sha);
828 
829     rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
830 
831     rawdata_to_hex (salt, salt_str, SHA256_DIGEST_LENGTH);
832 
833     /* Encode password hash related information into one string, similar to Django. */
834     GString *buf = g_string_new (NULL);
835     g_string_printf (buf, "PBKDF2SHA256$%d$%s$%s",
836                      iterations, salt_str, hashed_passwd);
837     *db_passwd = g_string_free (buf, FALSE);
838 }
839 
840 static gboolean
validate_passwd_pbkdf2_sha256(const char * passwd,const char * db_passwd)841 validate_passwd_pbkdf2_sha256 (const char *passwd, const char *db_passwd)
842 {
843     char **tokens;
844     char *salt_str, *hash;
845     int iter;
846     guint8 sha[SHA256_DIGEST_LENGTH];
847     guint8 salt[SHA256_DIGEST_LENGTH];
848     char hashed_passwd[SHA256_DIGEST_LENGTH*2+1];
849 
850     tokens = g_strsplit (db_passwd, "$", -1);
851     if (!tokens || g_strv_length (tokens) != 4) {
852         ccnet_warning ("Invalide db passwd format %s.\n", db_passwd);
853         return FALSE;
854     }
855 
856     iter = atoi (tokens[1]);
857     salt_str = tokens[2];
858     hash = tokens[3];
859 
860     hex_to_rawdata (salt_str, salt, SHA256_DIGEST_LENGTH);
861 
862     PKCS5_PBKDF2_HMAC (passwd, strlen(passwd),
863                        salt, sizeof(salt),
864                        iter,
865                        EVP_sha256(),
866                        sizeof(sha), sha);
867     rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH);
868 
869     gboolean ret = (strcmp (hash, hashed_passwd) == 0);
870 
871     g_strfreev (tokens);
872     return ret;
873 }
874 
875 static gboolean
validate_passwd(const char * passwd,const char * stored_passwd,gboolean * need_upgrade)876 validate_passwd (const char *passwd, const char *stored_passwd,
877                  gboolean *need_upgrade)
878 {
879     char hashed_passwd[SHA256_DIGEST_LENGTH * 2 + 1];
880     int hash_len = strlen(stored_passwd);
881 
882     *need_upgrade = FALSE;
883 
884     if (hash_len == SHA256_DIGEST_LENGTH * 2) {
885         hash_password_salted (passwd, hashed_passwd);
886         *need_upgrade = TRUE;
887     } else if (hash_len == SHA_DIGEST_LENGTH * 2) {
888         hash_password (passwd, hashed_passwd);
889         *need_upgrade = TRUE;
890     } else {
891         return validate_passwd_pbkdf2_sha256 (passwd, stored_passwd);
892     }
893 
894     if (strcmp (hashed_passwd, stored_passwd) == 0)
895         return TRUE;
896     else
897         return FALSE;
898 }
899 
900 static int
update_user_passwd(CcnetUserManager * manager,const char * email,const char * passwd)901 update_user_passwd (CcnetUserManager *manager,
902                     const char *email, const char *passwd)
903 {
904     CcnetDB *db = manager->priv->db;
905     char *db_passwd = NULL;
906     int ret;
907 
908     hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter,
909                                  &db_passwd);
910 
911     /* convert email to lower case for case insensitive lookup. */
912     char *email_down = g_ascii_strdown (email, strlen(email));
913 
914     ret = seaf_db_statement_query (db,
915                                     "UPDATE EmailUser SET passwd=? WHERE email=?",
916                                     2, "string", db_passwd, "string", email_down);
917 
918     g_free (db_passwd);
919     g_free (email_down);
920 
921     if (ret < 0)
922         return ret;
923 
924     return 0;
925 }
926 
927 int
ccnet_user_manager_add_emailuser(CcnetUserManager * manager,const char * email,const char * passwd,int is_staff,int is_active)928 ccnet_user_manager_add_emailuser (CcnetUserManager *manager,
929                                   const char *email,
930                                   const char *passwd,
931                                   int is_staff, int is_active)
932 {
933     CcnetDB *db = manager->priv->db;
934     gint64 now = get_current_time();
935     char *db_passwd = NULL;
936     int ret;
937 
938     if (!check_user_number (manager, FALSE)) {
939         return -1;
940     }
941 
942     /* A user with unhashed "!" as password cannot be logged in.
943      * Such users are created for book keeping, such as users from
944      * Shibboleth.
945      */
946     if (g_strcmp0 (passwd, "!") != 0)
947         hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter,
948                                      &db_passwd);
949     else
950         db_passwd = g_strdup(passwd);
951 
952     /* convert email to lower case for case insensitive lookup. */
953     char *email_down = g_ascii_strdown (email, strlen(email));
954 
955     ret = seaf_db_statement_query (db,
956                                     "INSERT INTO EmailUser(email, passwd, is_staff, "
957                                     "is_active, ctime) VALUES (?, ?, ?, ?, ?)",
958                                     5, "string", email_down, "string", db_passwd,
959                                     "int", is_staff, "int", is_active, "int64", now);
960 
961     g_free (db_passwd);
962     g_free (email_down);
963 
964     if (ret < 0)
965         return ret;
966 
967     return 0;
968 }
969 
970 int
ccnet_user_manager_remove_emailuser(CcnetUserManager * manager,const char * source,const char * email)971 ccnet_user_manager_remove_emailuser (CcnetUserManager *manager,
972                                      const char *source,
973                                      const char *email)
974 {
975     CcnetDB *db = manager->priv->db;
976     int ret;
977 
978     seaf_db_statement_query (db,
979                               "DELETE FROM UserRole WHERE email=?",
980                               1, "string", email);
981 
982     if (strcmp (source, "DB") == 0) {
983         ret = seaf_db_statement_query (db,
984                                         "DELETE FROM EmailUser WHERE email=?",
985                                         1, "string", email);
986         return ret;
987     }
988 
989 #ifdef HAVE_LDAP
990     if (strcmp (source, "LDAP") == 0 && manager->use_ldap) {
991         ret = seaf_db_statement_query (db,
992                                         "DELETE FROM LDAPUsers WHERE email=?",
993                                         1, "string", email);
994         return ret;
995     }
996 #endif
997 
998     return -1;
999 }
1000 
1001 static gboolean
get_password(CcnetDBRow * row,void * data)1002 get_password (CcnetDBRow *row, void *data)
1003 {
1004     char **p_passwd = data;
1005 
1006     *p_passwd = g_strdup(seaf_db_row_get_column_text (row, 0));
1007     return FALSE;
1008 }
1009 
1010 int
ccnet_user_manager_validate_emailuser(CcnetUserManager * manager,const char * email,const char * passwd)1011 ccnet_user_manager_validate_emailuser (CcnetUserManager *manager,
1012                                        const char *email,
1013                                        const char *passwd)
1014 {
1015     CcnetDB *db = manager->priv->db;
1016     int ret = -1;
1017     char *sql;
1018     char *email_down;
1019     char *login_id;
1020     char *stored_passwd = NULL;
1021     gboolean need_upgrade = FALSE;
1022 
1023     /* Users with password "!" are for internal book keeping only. */
1024     if (g_strcmp0 (passwd, "!") == 0)
1025         return -1;
1026 
1027     login_id = ccnet_user_manager_get_login_id (manager, email);
1028     if (!login_id) {
1029         ccnet_warning ("Failed to get login_id for %s\n", email);
1030         return -1;
1031     }
1032 
1033 #ifdef HAVE_LDAP
1034     if (manager->use_ldap) {
1035         if (ldap_verify_user_password (manager, login_id, passwd) == 0) {
1036             ret = 0;
1037             goto out;
1038         }
1039     }
1040 #endif
1041 
1042     sql = "SELECT passwd FROM EmailUser WHERE email=?";
1043     if (seaf_db_statement_foreach_row (db, sql,
1044                                         get_password, &stored_passwd,
1045                                         1, "string", login_id) > 0) {
1046         if (validate_passwd (passwd, stored_passwd, &need_upgrade)) {
1047             if (need_upgrade)
1048                 update_user_passwd (manager, login_id, passwd);
1049             ret = 0;
1050             goto out;
1051         } else {
1052             goto out;
1053         }
1054     }
1055 
1056     email_down = g_ascii_strdown (email, strlen(login_id));
1057     if (seaf_db_statement_foreach_row (db, sql,
1058                                         get_password, &stored_passwd,
1059                                         1, "string", email_down) > 0) {
1060         g_free (email_down);
1061         if (validate_passwd (passwd, stored_passwd, &need_upgrade)) {
1062             if (need_upgrade)
1063                 update_user_passwd (manager, login_id, passwd);
1064             ret = 0;
1065             goto out;
1066         } else {
1067             goto out;
1068         }
1069     }
1070     g_free (email_down);
1071 
1072 out:
1073 
1074     g_free (login_id);
1075     g_free (stored_passwd);
1076 
1077     return ret;
1078 }
1079 
1080 static gboolean
get_emailuser_cb(CcnetDBRow * row,void * data)1081 get_emailuser_cb (CcnetDBRow *row, void *data)
1082 {
1083     CcnetEmailUser **p_emailuser = data;
1084 
1085     int id = seaf_db_row_get_column_int (row, 0);
1086     const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
1087     int is_staff = seaf_db_row_get_column_int (row, 2);
1088     int is_active = seaf_db_row_get_column_int (row, 3);
1089     gint64 ctime = seaf_db_row_get_column_int64 (row, 4);
1090     const char *password = seaf_db_row_get_column_text (row, 5);
1091     const char *reference_id = seaf_db_row_get_column_text (row, 6);
1092     const char *role = seaf_db_row_get_column_text (row, 7);
1093 
1094     char *email_l = g_ascii_strdown (email, -1);
1095     *p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
1096                                  "id", id,
1097                                  "email", email_l,
1098                                  "is_staff", is_staff,
1099                                  "is_active", is_active,
1100                                  "ctime", ctime,
1101                                  "source", "DB",
1102                                  "password", password,
1103                                  "reference_id", reference_id,
1104                                  "role", role ? role : "",
1105                                  NULL);
1106     g_free (email_l);
1107 
1108     return FALSE;
1109 }
1110 
1111 static char*
1112 ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager,
1113                                      const char* email);
1114 
1115 static gboolean
get_ldap_emailuser_cb(CcnetDBRow * row,void * data)1116 get_ldap_emailuser_cb (CcnetDBRow *row, void *data)
1117 {
1118     CcnetEmailUser **p_emailuser = data;
1119 
1120     int id = seaf_db_row_get_column_int (row, 0);
1121     const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
1122     int is_staff = seaf_db_row_get_column_int (row, 2);
1123     int is_active = seaf_db_row_get_column_int (row, 3);
1124     const char *reference_id = seaf_db_row_get_column_text (row, 4);
1125     const char *role = seaf_db_row_get_column_text (row, 5);
1126 
1127     *p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
1128                                  "id", id,
1129                                  "email", email,
1130                                  "is_staff", is_staff,
1131                                  "is_active", is_active,
1132                                  "ctime", (gint64)0,
1133                                  "source", "LDAPImport",
1134                                  "password", "!",
1135                                  "reference_id", reference_id,
1136                                  "role", role ? role : "",
1137                                  NULL);
1138 
1139     return FALSE;
1140 }
1141 
1142 static CcnetEmailUser*
get_emailuser(CcnetUserManager * manager,const char * email,gboolean import)1143 get_emailuser (CcnetUserManager *manager,
1144                const char *email,
1145                gboolean import)
1146 {
1147     CcnetDB *db = manager->priv->db;
1148     char *sql;
1149     CcnetEmailUser *emailuser = NULL;
1150     char *email_down;
1151 
1152     sql = "SELECT e.id, e.email, is_staff, is_active, ctime, passwd, reference_id, role "
1153         " FROM EmailUser e LEFT JOIN UserRole ON e.email = UserRole.email "
1154         " WHERE e.email=?";
1155     if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1156                                         1, "string", email) > 0) {
1157         return emailuser;
1158     }
1159 
1160     email_down = g_ascii_strdown (email, strlen(email));
1161     if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1162                                         1, "string", email_down) > 0) {
1163         g_free (email_down);
1164         return emailuser;
1165     }
1166 
1167 #ifdef HAVE_LDAP
1168     if (manager->use_ldap) {
1169         int ret = seaf_db_statement_foreach_row (db,
1170                                                   "SELECT l.id, l.email, is_staff, is_active, "
1171                                                   "reference_id, role "
1172                                                   "FROM LDAPUsers l LEFT JOIN UserRole ON "
1173                                                   "l.email = UserRole.email WHERE l.email = ?",
1174                                                   get_ldap_emailuser_cb,
1175                                                   &emailuser, 1, "string", email_down);
1176         if (ret < 0) {
1177             ccnet_warning ("get ldapuser from db failed.\n");
1178             g_free (email_down);
1179             return NULL;
1180         }
1181 
1182         if (!emailuser) {
1183             GList *users, *ptr;
1184 
1185             users = ldap_list_users (manager, email, -1, -1);
1186             if (!users) {
1187                 /* Only print warning if this function is called in login. */
1188                 if (import)
1189                     ccnet_warning ("Cannot find user %s in LDAP.\n", email);
1190                 g_free (email_down);
1191                 return NULL;
1192             }
1193             emailuser = users->data;
1194 
1195             /* Free all except the first user. */
1196             for (ptr = users->next; ptr; ptr = ptr->next)
1197                 g_object_unref (ptr->data);
1198             g_list_free (users);
1199 
1200             if (import) {
1201                 if (!check_user_number (manager, FALSE)) {
1202                     g_free (email_down);
1203                     g_object_unref (emailuser);
1204                     return NULL;
1205                 }
1206 
1207                 // add user to LDAPUsers
1208                 ret = add_ldapuser (manager->priv->db, email_down, "",
1209                                     FALSE, TRUE, NULL);
1210                 if (ret < 0) {
1211                     ccnet_warning ("add ldapuser to db failed.\n");
1212                     g_free (email_down);
1213                     g_object_unref (emailuser);
1214                     return NULL;
1215                 }
1216 
1217                 g_object_set (emailuser, "id", ret, NULL);
1218             }
1219         }
1220 
1221         g_free (email_down);
1222         return emailuser;
1223     }
1224 #endif
1225 
1226     g_free (email_down);
1227 
1228     return NULL;
1229 
1230 }
1231 
1232 CcnetEmailUser*
ccnet_user_manager_get_emailuser(CcnetUserManager * manager,const char * email)1233 ccnet_user_manager_get_emailuser (CcnetUserManager *manager,
1234                                   const char *email)
1235 {
1236     return get_emailuser (manager, email, FALSE);
1237 }
1238 
1239 CcnetEmailUser*
ccnet_user_manager_get_emailuser_with_import(CcnetUserManager * manager,const char * email)1240 ccnet_user_manager_get_emailuser_with_import (CcnetUserManager *manager,
1241                                               const char *email)
1242 {
1243     return get_emailuser (manager, email, TRUE);
1244 }
1245 
1246 CcnetEmailUser*
ccnet_user_manager_get_emailuser_by_id(CcnetUserManager * manager,int id)1247 ccnet_user_manager_get_emailuser_by_id (CcnetUserManager *manager, int id)
1248 {
1249     CcnetDB *db = manager->priv->db;
1250     char *sql;
1251     CcnetEmailUser *emailuser = NULL;
1252 
1253     sql = "SELECT e.id, e.email, is_staff, is_active, ctime, passwd, reference_id, role "
1254         " FROM EmailUser e LEFT JOIN UserRole ON e.email = UserRole.email "
1255         " WHERE e.id=?";
1256     if (seaf_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser,
1257                                         1, "int", id) < 0)
1258         return NULL;
1259 
1260     return emailuser;
1261 }
1262 
1263 static gboolean
get_emailusers_cb(CcnetDBRow * row,void * data)1264 get_emailusers_cb (CcnetDBRow *row, void *data)
1265 {
1266     GList **plist = data;
1267     CcnetEmailUser *emailuser;
1268 
1269     int id = seaf_db_row_get_column_int (row, 0);
1270     const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
1271     int is_staff = seaf_db_row_get_column_int (row, 2);
1272     int is_active = seaf_db_row_get_column_int (row, 3);
1273     gint64 ctime = seaf_db_row_get_column_int64 (row, 4);
1274     const char *role = (const char *)seaf_db_row_get_column_text (row, 5);
1275     const char *password = seaf_db_row_get_column_text (row, 6);
1276 
1277     char *email_l = g_ascii_strdown (email, -1);
1278     emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
1279                               "id", id,
1280                               "email", email_l,
1281                               "is_staff", is_staff,
1282                               "is_active", is_active,
1283                               "ctime", ctime,
1284                               "role", role ? role : "",
1285                               "source", "DB",
1286                               "password", password,
1287                               NULL);
1288     g_free (email_l);
1289 
1290     *plist = g_list_prepend (*plist, emailuser);
1291 
1292     return TRUE;
1293 }
1294 
1295 static gboolean
get_ldap_emailusers_cb(CcnetDBRow * row,void * data)1296 get_ldap_emailusers_cb (CcnetDBRow *row, void *data)
1297 {
1298     GList **plist = data;
1299     CcnetEmailUser *emailuser = NULL;
1300 
1301     int id = seaf_db_row_get_column_int (row, 0);
1302     const char *email = (const char *)seaf_db_row_get_column_text (row, 1);
1303     int is_staff = seaf_db_row_get_column_int (row, 2);
1304     int is_active = seaf_db_row_get_column_int (row, 3);
1305     const char *role = seaf_db_row_get_column_text (row, 4);
1306 
1307     emailuser = g_object_new (CCNET_TYPE_EMAIL_USER,
1308                               "id", id,
1309                               "email", email,
1310                               "is_staff", is_staff,
1311                               "is_active", is_active,
1312                               "ctime", (gint64)0,
1313                               "role", role ? role : "",
1314                               "source", "LDAPImport",
1315                               "password", "!",
1316                               NULL);
1317     if (!emailuser)
1318         return FALSE;
1319 
1320     *plist = g_list_prepend (*plist, emailuser);
1321 
1322     return TRUE;
1323 }
1324 
1325 GList*
ccnet_user_manager_get_emailusers(CcnetUserManager * manager,const char * source,int start,int limit,const char * status)1326 ccnet_user_manager_get_emailusers (CcnetUserManager *manager,
1327                                    const char *source,
1328                                    int start, int limit,
1329                                    const char *status)
1330 {
1331     CcnetDB *db = manager->priv->db;
1332     const char *status_condition = "";
1333     char *sql = NULL;
1334     GList *ret = NULL;
1335     int rc;
1336 
1337 #ifdef HAVE_LDAP
1338     if (manager->use_ldap) {
1339         GList *users = NULL;
1340 
1341         if (g_strcmp0 (source, "LDAP") == 0) {
1342             users = ldap_list_users (manager, "*", start, limit);
1343             return g_list_reverse (users);
1344         } else if (g_strcmp0 (source, "LDAPImport") == 0) {
1345             if (start == -1 && limit == -1) {
1346                 if (g_strcmp0(status, "active") == 0)
1347                     status_condition = "WHERE t1.is_active = 1";
1348                 else if (g_strcmp0(status, "inactive") == 0)
1349                     status_condition = "WHERE t1.is_active = 0";
1350 
1351                 sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, "
1352                                        "t1.is_active, t2.role "
1353                                        "FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
1354                                        "ON t1.email = t2.email %s",
1355                                        status_condition);
1356 
1357                 rc = seaf_db_statement_foreach_row (db,
1358                                                      sql,
1359                                                      get_ldap_emailusers_cb,
1360                                                      &users, 0);
1361                 g_free (sql);
1362             } else {
1363                 if (g_strcmp0(status, "active") == 0)
1364                     status_condition = "WHERE t1.is_active = 1";
1365                 else if (g_strcmp0(status, "inactive") == 0)
1366                     status_condition = "WHERE t1.is_active = 0";
1367 
1368                 sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, "
1369                                        "t1.is_active, t2.role "
1370                                        "FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
1371                                        "ON t1.email = t2.email %s LIMIT ? OFFSET ?",
1372                                        status_condition);
1373 
1374                 rc = seaf_db_statement_foreach_row (db,
1375                                                      sql,
1376                                                      get_ldap_emailusers_cb,
1377                                                      &users, 2, "int", limit, "int", start);
1378                 g_free (sql);
1379             }
1380 
1381             if (rc < 0) {
1382                 while (users) {
1383                     g_object_unref (users->data);
1384                     users = g_list_delete_link (users, users);
1385                 }
1386                 return NULL;
1387             }
1388             return g_list_reverse (users);
1389         }
1390     }
1391 #endif
1392 
1393     if (g_strcmp0 (source, "DB") != 0)
1394         return NULL;
1395 
1396     if (start == -1 && limit == -1) {
1397         if (g_strcmp0(status, "active") == 0)
1398             status_condition = "WHERE t1.is_active = 1";
1399         else if (g_strcmp0(status, "inactive") == 0)
1400             status_condition = "WHERE t1.is_active = 0";
1401 
1402         sql = g_strdup_printf ("SELECT t1.id, t1.email, "
1403                                "t1.is_staff, t1.is_active, t1.ctime, "
1404                                "t2.role, t1.passwd FROM EmailUser t1 "
1405                                "LEFT JOIN UserRole t2 "
1406                                "ON t1.email = t2.email %s "
1407                                "WHERE t1.email NOT LIKE '%%@seafile_group'",
1408                                status_condition);
1409 
1410         rc = seaf_db_statement_foreach_row (db,
1411                                              sql,
1412                                              get_emailusers_cb, &ret,
1413                                              0);
1414         g_free (sql);
1415     } else {
1416         if (g_strcmp0(status, "active") == 0)
1417             status_condition = "WHERE t1.is_active = 1";
1418         else if (g_strcmp0(status, "inactive") == 0)
1419             status_condition = "WHERE t1.is_active = 0";
1420 
1421         sql = g_strdup_printf ("SELECT t1.id, t1.email, "
1422                                "t1.is_staff, t1.is_active, t1.ctime, "
1423                                "t2.role, t1.passwd FROM EmailUser t1 "
1424                                "LEFT JOIN UserRole t2 "
1425                                "ON t1.email = t2.email %s "
1426                                "WHERE t1.email NOT LIKE '%%@seafile_group' "
1427                                "ORDER BY t1.id LIMIT ? OFFSET ?",
1428                                status_condition);
1429 
1430         rc = seaf_db_statement_foreach_row (db,
1431                                              sql,
1432                                              get_emailusers_cb, &ret,
1433                                              2, "int", limit, "int", start);
1434         g_free (sql);
1435     }
1436 
1437     if (rc < 0) {
1438         while (ret != NULL) {
1439             g_object_unref (ret->data);
1440             ret = g_list_delete_link (ret, ret);
1441         }
1442         return NULL;
1443     }
1444 
1445     return g_list_reverse (ret);
1446 }
1447 
1448 GList*
ccnet_user_manager_search_emailusers(CcnetUserManager * manager,const char * source,const char * keyword,int start,int limit)1449 ccnet_user_manager_search_emailusers (CcnetUserManager *manager,
1450                                       const char *source,
1451                                       const char *keyword,
1452                                       int start, int limit)
1453 {
1454     CcnetDB *db = manager->priv->db;
1455     GList *ret = NULL;
1456     int rc;
1457     char *db_patt = g_strdup_printf ("%%%s%%", keyword);
1458 
1459 #ifdef HAVE_LDAP
1460     if (manager->use_ldap) {
1461         if (strcmp (source, "LDAP") == 0) {
1462             if (start == -1 && limit == -1) {
1463                 rc = seaf_db_statement_foreach_row (db,
1464                                                      "SELECT t1.id, t1.email, t1.is_staff, "
1465                                                      "t1.is_active, t2.role "
1466                                                      "FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
1467                                                      "ON t1.email = t2.email WHERE t1.email LIKE ?",
1468                                                      get_ldap_emailusers_cb,
1469                                                      &ret, 1, "string", db_patt);
1470             } else {
1471                 rc = seaf_db_statement_foreach_row (db,
1472                                                      "SELECT t1.id, t1.email, t1.is_staff, "
1473                                                      "t1.is_active, t2.role "
1474                                                      "FROM LDAPUsers t1 LEFT JOIN UserRole t2 "
1475                                                      "ON t1.email = t2.email WHERE t1.email LIKE ? "
1476                                                      "LIMIT ? OFFSET ?",
1477                                                      get_ldap_emailusers_cb,
1478                                                      &ret, 3, "string", db_patt,
1479                                                      "int", limit, "int", start);
1480             }
1481 
1482             g_free (db_patt);
1483 
1484             if (rc < 0) {
1485                 while (ret) {
1486                     g_object_unref (ret->data);
1487                     ret = g_list_delete_link (ret, ret);
1488                 }
1489                 return NULL;
1490             }
1491             return g_list_reverse (ret);
1492         }
1493     }
1494 #endif
1495 
1496     if (strcmp (source, "DB") != 0) {
1497         g_free (db_patt);
1498         return NULL;
1499     }
1500 
1501     if (start == -1 && limit == -1)
1502         rc = seaf_db_statement_foreach_row (db,
1503                                              "SELECT t1.id, t1.email, "
1504                                              "t1.is_staff, t1.is_active, t1.ctime, "
1505                                              "t2.role, t1.passwd FROM EmailUser t1 "
1506                                              "LEFT JOIN UserRole t2 "
1507                                              "ON t1.email = t2.email "
1508                                              "WHERE t1.Email LIKE ? "
1509                                              "AND t1.email NOT LIKE '%%@seafile_group' "
1510                                              "ORDER BY t1.id",
1511                                              get_emailusers_cb, &ret,
1512                                              1, "string", db_patt);
1513     else
1514         rc = seaf_db_statement_foreach_row (db,
1515                                              "SELECT t1.id, t1.email, "
1516                                              "t1.is_staff, t1.is_active, t1.ctime, "
1517                                              "t2.role, t1.passwd FROM EmailUser t1 "
1518                                              "LEFT JOIN UserRole t2 "
1519                                              "ON t1.email = t2.email "
1520                                              "WHERE t1.Email LIKE ? "
1521                                              "AND t1.email NOT LIKE '%%@seafile_group' "
1522                                              "ORDER BY t1.id LIMIT ? OFFSET ?",
1523                                              get_emailusers_cb, &ret,
1524                                              3, "string", db_patt,
1525                                              "int", limit, "int", start);
1526     g_free (db_patt);
1527     if (rc < 0) {
1528         while (ret != NULL) {
1529             g_object_unref (ret->data);
1530             ret = g_list_delete_link (ret, ret);
1531         }
1532         return NULL;
1533     }
1534 
1535     return g_list_reverse (ret);
1536 }
1537 
1538 GList*
ccnet_user_manager_search_ldapusers(CcnetUserManager * manager,const char * keyword,int start,int limit)1539 ccnet_user_manager_search_ldapusers (CcnetUserManager *manager,
1540                                      const char *keyword,
1541                                      int start, int limit)
1542 {
1543     GList *ret = NULL;
1544 
1545 #ifdef HAVE_LDAP
1546     if (!manager->use_ldap) {
1547         return NULL;
1548     }
1549 
1550     char *ldap_patt = g_strdup_printf ("*%s*", keyword);
1551 
1552     ret = ldap_list_users (manager, ldap_patt, start, limit);
1553 
1554     g_free (ldap_patt);
1555 #endif
1556 
1557     return ret;
1558 }
1559 
1560 gint64
ccnet_user_manager_count_emailusers(CcnetUserManager * manager,const char * source)1561 ccnet_user_manager_count_emailusers (CcnetUserManager *manager, const char *source)
1562 {
1563     CcnetDB* db = manager->priv->db;
1564     char sql[512];
1565     gint64 ret;
1566 
1567 #ifdef HAVE_LDAP
1568     if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) {
1569         gint64 ret = seaf_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 1");
1570         if (ret < 0)
1571             return -1;
1572         return ret;
1573     }
1574 #endif
1575 
1576     if (g_strcmp0 (source, "DB") != 0)
1577         return -1;
1578 
1579     snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 1");
1580 
1581     ret = seaf_db_get_int64 (db, sql);
1582     if (ret < 0)
1583         return -1;
1584     return ret;
1585 }
1586 
1587 gint64
ccnet_user_manager_count_inactive_emailusers(CcnetUserManager * manager,const char * source)1588 ccnet_user_manager_count_inactive_emailusers (CcnetUserManager *manager, const char *source)
1589 {
1590     CcnetDB* db = manager->priv->db;
1591     char sql[512];
1592     gint64 ret;
1593 
1594 #ifdef HAVE_LDAP
1595     if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) {
1596         gint64 ret = seaf_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 0");
1597         if (ret < 0)
1598             return -1;
1599         return ret;
1600     }
1601 #endif
1602 
1603     if (g_strcmp0 (source, "DB") != 0)
1604         return -1;
1605 
1606     snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 0");
1607 
1608     ret = seaf_db_get_int64 (db, sql);
1609     if (ret < 0)
1610         return -1;
1611     return ret;
1612 }
1613 
1614 #if 0
1615 GList*
1616 ccnet_user_manager_filter_emailusers_by_emails(CcnetUserManager *manager,
1617                                                const char *emails)
1618 {
1619     CcnetDB *db = manager->priv->db;
1620     char *copy = g_strdup (emails), *saveptr;
1621     GList *ret = NULL;
1622 
1623 #ifdef HAVE_LDAP
1624     if (manager->use_ldap)
1625         return NULL;            /* todo */
1626 #endif
1627 
1628     GString *sql = g_string_new(NULL);
1629 
1630     g_string_append (sql, "SELECT * FROM EmailUser WHERE Email IN (");
1631     char *name = strtok_r (copy, ", ", &saveptr);
1632     while (name != NULL) {
1633         g_string_append_printf (sql, "'%s',", name);
1634         name = strtok_r (NULL, ", ", &saveptr);
1635     }
1636     g_string_erase (sql, sql->len-1, 1); /* remove last "," */
1637     g_string_append (sql, ")");
1638 
1639     if (seaf_db_foreach_selected_row (db, sql->str, get_emailusers_cb,
1640         &ret) < 0) {
1641         while (ret != NULL) {
1642             g_object_unref (ret->data);
1643             ret = g_list_delete_link (ret, ret);
1644         }
1645         return NULL;
1646     }
1647 
1648     g_free (copy);
1649     g_string_free (sql, TRUE);
1650 
1651     return g_list_reverse (ret);
1652 }
1653 #endif
1654 
1655 int
ccnet_user_manager_update_emailuser(CcnetUserManager * manager,const char * source,int id,const char * passwd,int is_staff,int is_active)1656 ccnet_user_manager_update_emailuser (CcnetUserManager *manager,
1657                                      const char *source,
1658                                      int id, const char* passwd,
1659                                      int is_staff, int is_active)
1660 {
1661     CcnetDB* db = manager->priv->db;
1662     char *db_passwd = NULL;
1663 
1664     // in case set user user1 to inactive, then add another active user user2,
1665     // if current user num already the max user num,
1666     // then reset user1 to active should fail
1667     if (is_active && !check_user_number (manager, FALSE)) {
1668         return -1;
1669     }
1670 
1671     if (strcmp (source, "DB") == 0) {
1672         if (g_strcmp0 (passwd, "!") == 0) {
1673             /* Don't update passwd if it starts with '!' */
1674             return seaf_db_statement_query (db, "UPDATE EmailUser SET is_staff=?, "
1675                                              "is_active=? WHERE id=?",
1676                                              3, "int", is_staff, "int", is_active,
1677                                              "int", id);
1678         } else {
1679             hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter, &db_passwd);
1680 
1681             return seaf_db_statement_query (db, "UPDATE EmailUser SET passwd=?, "
1682                                              "is_staff=?, is_active=? WHERE id=?",
1683                                              4, "string", db_passwd, "int", is_staff,
1684                                              "int", is_active, "int", id);
1685         }
1686     }
1687 
1688 #ifdef HAVE_LDAP
1689     if (manager->use_ldap && strcmp (source, "LDAP") == 0) {
1690         return seaf_db_statement_query (db, "UPDATE LDAPUsers SET is_staff=?, "
1691                                          "is_active=? WHERE id=?",
1692                                          3, "int", is_staff, "int", is_active,
1693                                          "int", id);
1694     }
1695 #endif
1696 
1697     return -1;
1698 }
1699 
1700 static gboolean
get_role_emailuser_cb(CcnetDBRow * row,void * data)1701 get_role_emailuser_cb (CcnetDBRow *row, void *data)
1702 {
1703     *((char **)data) = g_strdup (seaf_db_row_get_column_text (row, 0));
1704 
1705     return FALSE;
1706 }
1707 
1708 static char*
ccnet_user_manager_get_role_emailuser(CcnetUserManager * manager,const char * email)1709 ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager,
1710                                      const char* email)
1711 {
1712 
1713     CcnetDB *db = manager->priv->db;
1714     const char *sql;
1715     char* role;
1716 
1717     sql = "SELECT role FROM UserRole WHERE email=?";
1718     if (seaf_db_statement_foreach_row (db, sql, get_role_emailuser_cb, &role,
1719                                         1, "string", email) > 0)
1720         return role;
1721 
1722     return NULL;
1723 }
1724 
1725 int
ccnet_user_manager_update_role_emailuser(CcnetUserManager * manager,const char * email,const char * role)1726 ccnet_user_manager_update_role_emailuser (CcnetUserManager *manager,
1727                                      const char* email, const char* role)
1728 {
1729     CcnetDB* db = manager->priv->db;
1730     char *old_role = ccnet_user_manager_get_role_emailuser (manager, email);
1731     if (old_role) {
1732         g_free (old_role);
1733         return seaf_db_statement_query (db, "UPDATE UserRole SET role=? "
1734                                          "WHERE email=?",
1735                                          2, "string", role, "string", email);
1736     } else
1737         return seaf_db_statement_query (db, "INSERT INTO UserRole(role, email)"
1738                                          " VALUES (?, ?)",
1739                                          2, "string", role, "string", email);
1740 }
1741 
1742 GList*
ccnet_user_manager_get_superusers(CcnetUserManager * manager)1743 ccnet_user_manager_get_superusers(CcnetUserManager *manager)
1744 {
1745     CcnetDB* db = manager->priv->db;
1746     GList *ret = NULL;
1747     char sql[512];
1748 
1749     snprintf (sql, 512,
1750               "SELECT t1.id, t1.email, "
1751               "t1.is_staff, t1.is_active, t1.ctime, "
1752               "t2.role, t1.passwd FROM EmailUser t1 "
1753               "LEFT JOIN UserRole t2 "
1754               "ON t1.email = t2.email "
1755               "WHERE is_staff = 1 AND t1.email NOT LIKE '%%@seafile_group';");
1756 
1757     if (seaf_db_foreach_selected_row (db, sql, get_emailusers_cb, &ret) < 0) {
1758         while (ret != NULL) {
1759             g_object_unref (ret->data);
1760             ret = g_list_delete_link (ret, ret);
1761         }
1762         return NULL;
1763     }
1764 
1765     if (seaf_db_foreach_selected_row (db,
1766                                        "SELECT t1.id, t1.email, "
1767                                        "t1.is_staff, t1.is_active, "
1768                                        "t2.role FROM LDAPUsers t1 "
1769                                        "LEFT JOIN UserRole t2 "
1770                                        "ON t1.email = t2.email "
1771                                        "WHERE is_staff = 1",
1772                                        get_ldap_emailusers_cb, &ret) < 0) {
1773         while (ret != NULL) {
1774             g_object_unref (ret->data);
1775             ret = g_list_delete_link (ret, ret);
1776         }
1777         return NULL;
1778     }
1779 
1780     return g_list_reverse (ret);
1781 }
1782 
1783 int
ccnet_user_manager_set_reference_id(CcnetUserManager * manager,const char * primary_id,const char * reference_id,GError ** error)1784 ccnet_user_manager_set_reference_id (CcnetUserManager *manager,
1785                                    const char *primary_id,
1786                                    const char *reference_id,
1787                                    GError **error)
1788 {
1789     int rc;
1790     char *sql;
1791     gboolean exists, err;
1792 
1793 #ifdef HAVE_LDAP
1794     if (manager->use_ldap) {
1795         sql = "SELECT email FROM LDAPUsers WHERE email = ?";
1796         exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1797                                             1, "string", primary_id);
1798         if (err)
1799             return -1;
1800         /* Make sure reference_id is unique */
1801         if (exists) {
1802             sql = "SELECT 1 FROM EmailUser e, LDAPUsers l "
1803                   "WHERE (e.reference_id=? AND e.email!=?) OR "
1804                   "(l.reference_id=? AND l.email!=?) OR "
1805                   "(e.email=? AND e.email!=?) OR (l.email=? AND l.email!=?)";
1806             exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1807                                                 8, "string", reference_id,
1808                                                 "string", primary_id,
1809                                                 "string", reference_id,
1810                                                 "string", primary_id,
1811                                                 "string", reference_id,
1812                                                 "string", primary_id,
1813                                                 "string", reference_id,
1814                                                 "string", primary_id);
1815             if (err)
1816                 return -1;
1817             if (exists) {
1818                 ccnet_warning ("Failed to set reference id, email '%s' exists\n", reference_id);
1819                 return -1;
1820             }
1821 
1822             sql = "UPDATE LDAPUsers SET reference_id=? WHERE email=?";
1823             rc = seaf_db_statement_query (manager->priv->db, sql, 2,
1824                                            "string", reference_id, "string", primary_id);
1825             if (rc < 0){
1826                 ccnet_warning ("Failed to set reference id for '%s'\n", primary_id);
1827             }
1828             return rc;
1829         }
1830     }
1831 #endif
1832 
1833     sql = "SELECT email FROM EmailUser WHERE email = ?";
1834     exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1835                                         1, "string", primary_id);
1836     if (err)
1837         return -1;
1838     /* Make sure reference_id is unique */
1839     if (exists) {
1840         sql = "SELECT 1 FROM EmailUser e, LDAPUsers l "
1841               "WHERE (e.reference_id=? AND e.email!=?) OR "
1842               "(l.reference_id=? AND l.email!=?) OR "
1843               "(e.email=? AND e.email!=?) OR (l.email=? AND l.email!=?)";
1844         exists = seaf_db_statement_exists (manager->priv->db, sql, &err,
1845                                             8, "string", reference_id,
1846                                             "string", primary_id,
1847                                             "string", reference_id,
1848                                             "string", primary_id,
1849                                             "string", reference_id,
1850                                             "string", primary_id,
1851                                             "string", reference_id,
1852                                             "string", primary_id);
1853         if (err)
1854             return -1;
1855         if (exists) {
1856             ccnet_warning ("Failed to set reference id, email '%s' exists\n", reference_id);
1857             return -1;
1858         }
1859 
1860         sql = "UPDATE EmailUser SET reference_id=? WHERE email=?";
1861             rc = seaf_db_statement_query (manager->priv->db, sql, 2,
1862                                            "string", reference_id, "string", primary_id);
1863         if (rc < 0){
1864             ccnet_warning ("Failed to set reference id for %s\n", primary_id);
1865             return -1;
1866         }
1867         return rc;
1868     } else {
1869         ccnet_warning ("Failed to set reference id, Primary id '%s' not exists\n", primary_id);
1870         return -1;
1871     }
1872 }
1873 
1874 char *
ccnet_user_manager_get_primary_id(CcnetUserManager * manager,const char * email)1875 ccnet_user_manager_get_primary_id (CcnetUserManager *manager, const char *email)
1876 {
1877     char *sql;
1878     char *primary_id = NULL;
1879 
1880 #ifdef HAVE_LDAP
1881     if (manager->use_ldap) {
1882         sql = "SELECT email FROM LDAPUsers WHERE reference_id=?";
1883         primary_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", email);
1884         if (primary_id)
1885             return primary_id;
1886     }
1887 #endif
1888 
1889     sql = "SELECT email FROM EmailUser WHERE reference_id=?";
1890     primary_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", email);
1891     if (primary_id)
1892         return primary_id;
1893     else
1894         return NULL;
1895 }
1896 
1897 char *
ccnet_user_manager_get_login_id(CcnetUserManager * manager,const char * primary_id)1898 ccnet_user_manager_get_login_id (CcnetUserManager *manager, const char *primary_id)
1899 {
1900 #ifdef HAVE_LDAP
1901     if (manager->use_ldap) {
1902         char *sql = "SELECT reference_id FROM LDAPUsers WHERE email=?";
1903         char *ldap_login_id = seaf_db_statement_get_string (manager->priv->db, sql, 1, "string", primary_id);
1904 
1905         if (ldap_login_id)
1906             return ldap_login_id;
1907     }
1908 #endif
1909     return g_strdup (primary_id);
1910 }
1911 
1912 GList *
ccnet_user_manager_get_emailusers_in_list(CcnetUserManager * manager,const char * source,const char * user_list,GError ** error)1913 ccnet_user_manager_get_emailusers_in_list (CcnetUserManager *manager,
1914                                            const char *source,
1915                                            const char *user_list,
1916                                            GError **error)
1917 {
1918     int i;
1919     const char *username;
1920     json_t *j_array = NULL, *j_obj;
1921     json_error_t j_error;
1922     GList *ret = NULL;
1923     const char *args[20];
1924 
1925     j_array = json_loadb (user_list, strlen(user_list), 0, &j_error);
1926     if (!j_array) {
1927         g_set_error (error, CCNET_DOMAIN, 0, "Bad args.");
1928         return NULL;
1929     }
1930     /* Query 20 users at most. */
1931     size_t user_num = json_array_size (j_array);
1932     if (user_num > 20) {
1933         g_set_error (error, CCNET_DOMAIN, 0, "Number of users exceeds 20.");
1934         json_decref (j_array);
1935         return NULL;
1936     }
1937     GString *sql = g_string_new ("");
1938     for (i = 0; i < 20; i++) {
1939         if (i < user_num) {
1940             j_obj = json_array_get (j_array, i);
1941             username = json_string_value(j_obj);
1942             args[i] = username;
1943         } else {
1944             args[i] = "";
1945         }
1946     }
1947 
1948 #ifdef HAVE_LDAP
1949     if (manager->use_ldap) {
1950         if (strcmp (source, "LDAP") == 0) {
1951             g_string_printf (sql, "SELECT l.id, l.email, is_staff, is_active, role "
1952                                   "FROM LDAPUsers l LEFT JOIN UserRole r "
1953                                   "ON l.email = r.email "
1954                                   "WHERE l.email IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
1955             if (seaf_db_statement_foreach_row (manager->priv->db, sql->str, get_ldap_emailusers_cb, &ret, 20,
1956                                         "string", args[0], "string", args[1], "string", args[2],
1957                                         "string", args[3], "string", args[4], "string", args[5],
1958                                         "string", args[6], "string", args[7], "string", args[8],
1959                                         "string", args[9], "string", args[10], "string", args[11],
1960                                         "string", args[12], "string", args[13], "string", args[14],
1961                                         "string", args[15], "string", args[16], "string", args[17],
1962                                         "string", args[18], "string", args[19]) < 0)
1963                 ccnet_warning("Failed to get users in list %s.\n", user_list);
1964 
1965             goto out;
1966         }
1967     }
1968 #endif
1969     if (strcmp (source, "DB") != 0)
1970         goto out;
1971 
1972     g_string_printf (sql, "SELECT e.id, e.email, is_staff, is_active, ctime, "
1973                           "role, passwd FROM EmailUser e "
1974                           "LEFT JOIN UserRole r ON e.email = r.email "
1975                           "WHERE e.email IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
1976 
1977     if (seaf_db_statement_foreach_row (manager->priv->db, sql->str, get_emailusers_cb, &ret, 20,
1978                                         "string", args[0], "string", args[1], "string", args[2],
1979                                         "string", args[3], "string", args[4], "string", args[5],
1980                                         "string", args[6], "string", args[7], "string", args[8],
1981                                         "string", args[9], "string", args[10], "string", args[11],
1982                                         "string", args[12], "string", args[13], "string", args[14],
1983                                         "string", args[15], "string", args[16], "string", args[17],
1984                                         "string", args[18], "string", args[19]) < 0)
1985         ccnet_warning("Failed to get users in list %s.\n", user_list);
1986 
1987 out:
1988     json_decref (j_array);
1989     g_string_free (sql, TRUE);
1990 
1991     return ret;
1992 }
1993 
1994 int
ccnet_user_manager_update_emailuser_id(CcnetUserManager * manager,const char * old_email,const char * new_email,GError ** error)1995 ccnet_user_manager_update_emailuser_id (CcnetUserManager *manager,
1996                                         const char *old_email,
1997                                         const char *new_email,
1998                                         GError **error)
1999 {
2000     int ret = -1;
2001     int rc;
2002     GString *sql = g_string_new ("");
2003 
2004     //1.update RepoOwner
2005     g_string_printf (sql, "UPDATE RepoOwner SET owner_id=? WHERE owner_id=?");
2006     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2007                                   "string", new_email,
2008                                   "string", old_email);
2009     if (rc < 0){
2010         ccnet_warning ("Failed to update repo owner\n");
2011         goto out;
2012     }
2013 
2014     //2.update SharedRepo
2015     g_string_printf (sql, "UPDATE SharedRepo SET from_email=? WHERE from_email=?");
2016     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2017                                   "string", new_email,
2018                                   "string", old_email);
2019     if (rc < 0){
2020         ccnet_warning ("Failed to update from_email\n");
2021         goto out;
2022     }
2023 
2024     g_string_printf (sql, "UPDATE SharedRepo SET to_email=? WHERE to_email=?");
2025     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2026                                   "string", new_email,
2027                                   "string", old_email);
2028     if (rc < 0){
2029         ccnet_warning ("Failed to update to_email\n");
2030         goto out;
2031     }
2032 
2033     //3.update GroupUser
2034     rc = ccnet_group_manager_update_group_user (seaf->group_mgr, old_email, new_email);
2035     if (rc < 0){
2036         ccnet_warning ("Failed to update group member\n");
2037         goto out;
2038     }
2039 
2040     //4.update RepoUserToken
2041     g_string_printf (sql, "UPDATE RepoUserToken SET email=? WHERE email=?");
2042     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2043                                   "string", new_email,
2044                                   "string", old_email);
2045     if (rc < 0){
2046         ccnet_warning ("Failed to update repo user token\n");
2047         goto out;
2048     }
2049 
2050     //5.uptede FolderUserPerm
2051     g_string_printf (sql, "UPDATE FolderUserPerm SET user=? WHERE user=?");
2052     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2053                                   "string", new_email,
2054                                   "string", old_email);
2055     if (rc < 0){
2056         ccnet_warning ("Failed to update user folder permission\n");
2057         goto out;
2058     }
2059 
2060     //6.update EmailUser
2061     g_string_printf (sql, "UPDATE EmailUser SET email=? WHERE email=?");
2062     rc = seaf_db_statement_query (manager->priv->db, sql->str, 2,
2063                                   "string", new_email,
2064                                   "string", old_email);
2065     if (rc < 0){
2066         ccnet_warning ("Failed to update email user\n");
2067         goto out;
2068     }
2069 
2070     g_string_printf (sql, "UPDATE LDAPUsers SET email=? WHERE email=?");
2071     rc = seaf_db_statement_query (manager->priv->db, sql->str, 2,
2072                                   "string", new_email,
2073                                   "string", old_email);
2074     if (rc < 0){
2075         ccnet_warning ("Failed to update LDAP user\n");
2076         goto out;
2077     }
2078     //7.update UserQuota
2079     g_string_printf (sql, "UPDATE UserQuota SET user=? WHERE user=?");
2080     rc = seaf_db_statement_query (seaf->db, sql->str, 2,
2081                                   "string", new_email,
2082                                   "string", old_email);
2083     if (rc < 0){
2084         ccnet_warning ("Failed to update user quota\n");
2085         goto out;
2086     }
2087 
2088     ret = 0;
2089 out:
2090     g_string_free (sql, TRUE);
2091     return ret;
2092 }
2093