system-controller: do mkdir() calls persistently.
[profile/ivi/murphy.git] / src / plugins / system-controller / user / user.c
1 /*
2  * Copyright (c) 2014, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <fcntl.h>
39
40 #include <murphy/common.h>
41 #include <murphy/common/process.h>
42 #include <murphy/core/lua-bindings/murphy.h>
43 #include <murphy/core/lua-utils/error.h>
44 #include <murphy/core/lua-utils/object.h>
45 #include <murphy/core/lua-utils/funcbridge.h>
46
47 #include <murphy-db/mqi.h>
48 #include <murphy-db/mql.h>
49
50 #include <lualib.h>
51 #include <lauxlib.h>
52
53 #include <libxml/parser.h>
54 #include <libxml/tree.h>
55
56 #include <aul/aul.h>
57 #include <bundle.h>
58
59 #include "user.h"
60
61 #define USER_MANAGER_CLASS MRP_LUA_CLASS(user_manager, lua)
62
63 /* TODO: associate the two structs with each other */
64
65 typedef struct {
66
67 } user_manager_t;
68
69 typedef struct {
70     char *user_dir_base;
71     pid_t current_hs_pid;
72     char *defaultapps_path;
73     char *flag_file_path;
74
75     mrp_list_hook_t users;
76     mrp_mainloop_t *ml;
77     mrp_timer_t *t;
78     int homescreen_count;
79
80     user_manager_t *mgr;
81 } user_manager_config_t;
82
83 typedef struct {
84     char *name;
85     char *passwd;
86     char *homescreen;
87     char *user_dir;
88     char *runningapp_path;
89
90     user_manager_config_t *ctx;
91     mrp_list_hook_t hook;
92 } user_config_t;
93
94 /* TODO: move current_user to ctx */
95 static user_config_t *current_user;
96
97 /* TODO: move users to ctx */
98 static mrp_list_hook_t users;
99
100 static int user_manager_create(lua_State *L);
101 static void user_manager_destroy(void *ctx);
102
103 static int lua_set_lastinfo(lua_State *L);
104 static int lua_get_lastinfo(lua_State *L);
105 static int lua_get_userlist(lua_State *L);
106 static int lua_change_user(lua_State *L);
107
108 #if 0
109 static void hs_check(mrp_timer_t *t, void *user_data);
110 #endif
111
112 MRP_LUA_METHOD_LIST_TABLE(user_manager_methods,
113                           MRP_LUA_METHOD_CONSTRUCTOR(user_manager_create)
114                           MRP_LUA_METHOD(getUserList, lua_get_userlist)
115                           MRP_LUA_METHOD(setLastinfo, lua_set_lastinfo)
116                           MRP_LUA_METHOD(getLastinfo, lua_get_lastinfo)
117                           MRP_LUA_METHOD(changeUser, lua_change_user));
118
119 MRP_LUA_METHOD_LIST_TABLE(user_manager_overrides,
120                           MRP_LUA_OVERRIDE_CALL     (user_manager_create));
121
122 MRP_LUA_DEFINE_CLASS(user_manager, lua, user_manager_t, user_manager_destroy,
123         user_manager_methods, user_manager_overrides, NULL, NULL, NULL, NULL, NULL,
124         MRP_LUA_CLASS_EXTENSIBLE);
125
126 static user_manager_t *user_manager_check(lua_State *L, int idx)
127 {
128     return (user_manager_t *) mrp_lua_check_object(L, USER_MANAGER_CLASS, idx);
129 }
130
131 static void user_manager_destroy(void *ctx)
132 {
133     MRP_UNUSED(ctx);
134     mrp_log_info("user_manager_destroy");
135 }
136
137 static int user_manager_create(lua_State *L)
138 {
139     user_manager_t *ctx = NULL;
140
141     ctx = (user_manager_t *) mrp_lua_create_object(L, USER_MANAGER_CLASS, NULL,
142             0);
143
144     mrp_lua_push_object(L, ctx);
145
146     return 1;
147 }
148
149 static bool mkdir_if_needed(const char *dir)
150 {
151     mrp_debug("creating directory '%s'", dir);
152
153     if (mkdir(dir, 0700) == -1) {
154         struct stat buf;
155
156         if (errno != EEXIST) {
157             mrp_log_error("failed to create directory '%s'", dir);
158             goto error;
159         }
160
161         /* there already is a directory or a file of that name */
162
163         if (stat(dir, &buf) == -1) {
164             mrp_log_error("failed to stat directory '%s'", dir);
165             goto error;
166         }
167
168         if (!S_ISDIR(buf.st_mode)) {
169             mrp_log_error("file '%s' exists and isn't a directory", dir);
170             goto error;
171         }
172     }
173     return TRUE;
174
175 error:
176     return FALSE;
177 }
178
179
180 static bool verify_appid(const char *appid)
181 {
182     /* check if appid contains illegal elements, since it's being used in
183        paths */
184
185     /* allowed are [a-zA-Z0-9.-_] TODO: might be faster to do this with a
186        lookup table */
187
188     int i;
189     int len;
190
191     if (!appid)
192         return FALSE;
193
194     len = strlen(appid);
195
196     if (len == 0)
197         return FALSE;
198
199     for (i = 0; i < len; i++) {
200         char c = appid[i];
201
202         if (c >= 'a' && c <= 'z')
203             continue;
204
205         if (c >= 'A' && c <= 'Z')
206             continue;
207
208         if (c >= '0' && c <= '9')
209             continue;
210
211         if (c == '.' || c == '-' || c == '_')
212             continue;
213
214         return FALSE;
215     }
216
217     return TRUE;
218 }
219
220 /* lua_get_userlist returns a list of users in the system. */
221
222 static int lua_get_userlist(lua_State *L)
223 {
224     mrp_list_hook_t *p, *n;
225     user_config_t *config;
226     int i = 1, narg;
227     user_manager_t *mgr;
228
229     narg = lua_gettop(L);
230
231     if (narg != 1)
232         return luaL_error(L, "expecting no arguments");
233
234     mgr = user_manager_check(L, 1);
235
236     if (!mgr)
237         return luaL_error(L, "no self pointer");
238
239     /* make a user table and push it to the stack */
240
241     lua_newtable(L);
242
243     /* push all user strings to a table */
244
245     mrp_list_foreach(&users, p, n) {
246         config = mrp_list_entry(p, typeof(*config), hook);
247
248         mrp_debug("read user '%s'", config->name);
249
250         lua_pushinteger(L, i++);
251         lua_pushstring(L, config->name);
252         lua_settable(L, -3);
253     }
254
255     /* push current user to stack */
256
257     if (current_user)
258         lua_pushstring(L, current_user->name);
259     else
260         lua_pushnil(L);
261
262     return 2;
263 }
264
265
266 static int terminate_app(const aul_app_info *ai, void *user_data)
267 {
268     int ret = 0;
269
270     MRP_UNUSED(user_data);
271
272     if (!ai)
273         return 0;
274
275     mrp_log_info("terminate %s", ai->appid ? ai->appid : "unknown application");
276
277     /* org.tizen.ico.homescreen, org.tizen.ico.statusbar, others */
278
279     ret = aul_terminate_pid(ai->pid);
280     mrp_log_info("termination %s",
281             ret < 0 ? "not successful" : "successful");
282
283     return 0;
284 }
285
286 #if 0
287 static int iter_aul_app_info(const aul_app_info *ai, void *user_data)
288 {
289     pid_t *hs_pid = (pid_t *) user_data;
290
291     MRP_UNUSED(user_data);
292
293     if (!ai || !ai->appid)
294         return 0;
295
296     mrp_log_info("ai: pid %i, appid: %s, pkg_name %s", ai->pid, ai->appid,
297             ai->pkg_name ? ai->pkg_name : "NULL");
298
299     if (current_user && strcmp(current_user->homescreen, ai->appid) == 0) {
300         *hs_pid = ai->pid;
301     }
302
303     return 0;
304 }
305
306 static pid_t get_hs_pid(const char *homescreen)
307 {
308     pid_t hs_pid = -1;
309
310     if (!aul_app_is_running(homescreen))
311         return -1;
312
313     aul_app_get_running_app_info(iter_aul_app_info, &hs_pid);
314
315     return hs_pid;
316 }
317 #endif
318
319 static bool launch_hs(user_manager_config_t *ctx)
320 {
321     bundle *b = bundle_create();
322
323     if (!b) {
324         mrp_log_error("could not create bundle");
325         return FALSE;
326     }
327
328     if (!current_user) {
329         bundle_free(b);
330         return FALSE;
331     }
332
333     bundle_add(b, "HS_PARAM_U", current_user->user_dir);
334     bundle_add(b, "HS_PARAM_D", current_user->runningapp_path);
335     bundle_add(b, "HS_PARAM_DD", ctx->defaultapps_path);
336     bundle_add(b, "HS_PARAM_FLG", ctx->flag_file_path);
337
338     mrp_log_info("launching new homescreen for %s, parameters: %s %s %s %s",
339             current_user->name, current_user->user_dir,
340             current_user->runningapp_path, ctx->defaultapps_path,
341             ctx->flag_file_path);
342
343     ctx->current_hs_pid = aul_launch_app(current_user->homescreen, b);
344
345     bundle_free(b);
346
347     return TRUE;
348 }
349
350 static void create_flag_file(user_manager_config_t *ctx)
351 {
352     FILE *f = fopen(ctx->flag_file_path, "w");
353     if (f)
354         fclose(f);
355 }
356
357 static void delete_flag_file(user_manager_config_t *ctx)
358 {
359     unlink(ctx->flag_file_path);
360 }
361
362 static void launch_hs_deferred(mrp_timer_t *t, void *user_data)
363 {
364     user_manager_config_t *ctx = (user_manager_config_t *) user_data;
365
366     launch_hs(ctx);
367
368     /* flag file off */
369
370     delete_flag_file(ctx);
371
372     mrp_del_timer(t);
373 }
374
375 /* change_user authenticates the user, destroys the current session and starts
376  * a new session with the new user. */
377
378 static bool change_user(const char *user, const char *passwd)
379 {
380     mrp_list_hook_t *p, *n;
381     user_config_t *config;
382     user_manager_config_t *ctx;
383
384     if (strcmp(user, "") == 0 && strcmp(passwd, "") == 0) {
385         /* this is a logout */
386
387         if (!current_user) {
388             mrp_log_error("trying to log out a non-existing user");
389             return FALSE;
390         }
391
392         ctx = current_user->ctx;
393
394         current_user = NULL;
395
396         /* get the current homescreen pid */
397
398         if (ctx->current_hs_pid > 0) {
399             /* terminate the current homescreen and all other applications */
400             mrp_log_info("terminating homescreen %i", ctx->current_hs_pid);
401             aul_app_get_running_app_info(terminate_app, ctx);
402             ctx->current_hs_pid = -1;
403         }
404
405         return TRUE;
406     }
407
408     mrp_list_foreach(&users, p, n) {
409         config = mrp_list_entry(p, typeof(*config), hook);
410
411         ctx = config->ctx;
412
413         if (strcmp(user, config->name) == 0 &&
414             strcmp(passwd, config->passwd) == 0) {
415
416             mrp_timer_t *t;
417
418             /* "authenticated" now */
419             mrp_log_info("authenticated user %s", user);
420
421             if (current_user && strcmp(user, current_user->name) == 0) {
422                 mrp_log_warning("user '%s' is already logged in", user);
423                 return TRUE;
424             }
425
426             /* flag file on */
427
428             create_flag_file(ctx);
429
430             /* TODO: save current state meaning running programs */
431
432             /* change the current user */
433
434             current_user = config;
435
436             /* get the current homescreen pid */
437
438             if (ctx->current_hs_pid > 0) {
439                 /* terminate the current homescreen and other applications */
440                 aul_app_get_running_app_info(terminate_app, ctx);
441                 ctx->current_hs_pid = -1;
442             }
443
444             /* launch new homescreen, wait the system controller amount :-) */
445
446             /* TODO: should be done from dead_signal handler, but that doesn't
447                appear to be working properly */
448
449             t = mrp_add_timer(ctx->ml, 2000, launch_hs_deferred, ctx);
450
451             if (!t)
452                 return FALSE;
453
454             return TRUE;
455         }
456     }
457     return FALSE;
458 }
459
460 static int lua_change_user(lua_State *L)
461 {
462     const char *user;
463     const char *passwd;
464     int narg;
465     user_manager_t *ctx;
466
467     narg = lua_gettop(L);
468
469     if (narg != 3)
470         return luaL_error(L, "expecting two arguments");
471
472     ctx = user_manager_check(L, 1);
473
474     if (!ctx)
475         return luaL_error(L, "no self pointer");
476
477     if (!lua_isstring(L, -1) || !lua_isstring(L, -2))
478         return luaL_error(L, "argument error");
479
480     user = lua_tostring(L, -2);
481
482     if (!user)
483         return luaL_error(L, "string error");
484
485     passwd = lua_tostring(L, -1);
486
487     if (!passwd)
488         return luaL_error(L, "string error");
489
490     mrp_log_info("lua_change_user '%s'", user);
491
492     if (change_user(user, passwd))
493         lua_pushboolean(L, 1);
494     else
495         lua_pushboolean(L, 0);
496
497     return 1;
498 }
499
500 /* set_lastinfo stores the lastinfo string to a file, whose name
501    is derived from string appid. */
502
503 static bool set_lastinfo(lua_State *L, const char *appid, const char *lastinfo,
504         size_t lastinfo_len)
505 {
506     char path[256];
507     int appid_len = strlen(appid);
508     int fd, base_len;
509     char *base;
510
511     MRP_UNUSED(L);
512
513     if (!verify_appid(appid))
514         return FALSE;
515
516     if (!current_user)
517         return FALSE;
518
519     base = current_user->user_dir;
520
521     if (!base)
522         return FALSE;
523
524     base_len = strlen(base);
525
526     if (base_len + 1 + appid_len >= 256)
527         return FALSE;
528
529     strcpy(path, base);
530     path[base_len] = '/';
531     strcpy(path+base_len+1, appid);
532     path[base_len + 1 + appid_len] = '\0';
533
534     mrp_log_info("writing lastinfo to '%s'\n", path);
535
536     fd = open(path, O_WRONLY | O_CREAT, 0600);
537
538     if (fd < 0) {
539         mrp_log_error("failed to open lastinfo file for writing");
540         return FALSE;
541     }
542
543     if (write(fd, lastinfo, lastinfo_len) < 0) {
544         mrp_log_error("failed to write to lastinfo file");
545         close(fd);
546         return FALSE;
547     }
548
549     close(fd);
550
551     return TRUE;
552 }
553
554 static int lua_set_lastinfo(lua_State *L)
555 {
556     const char *appid;
557     const char *lastinfo;
558     int narg;
559     user_manager_t *ctx;
560     size_t lastinfo_len;
561
562     narg = lua_gettop(L);
563
564     if (narg != 3)
565         return luaL_error(L, "expecting two arguments");
566
567     ctx = user_manager_check(L, 1);
568
569     if (!ctx)
570         return luaL_error(L, "no self pointer");
571
572     if (!lua_isstring(L, -1) || !lua_isstring(L, -2))
573         goto error;
574
575     lastinfo = lua_tostring(L, -2);
576
577     if (!lastinfo)
578         goto error;
579
580     lastinfo_len = lua_strlen(L, -2);
581
582     appid = lua_tostring(L, -1);
583
584     if (!appid)
585         goto error;
586
587     mrp_log_info("set_lastinfo appid: '%s', lastinfo: '%s'", appid, lastinfo);
588
589     if (!set_lastinfo(L, appid, lastinfo, lastinfo_len))
590         goto error;
591
592     return 1;
593
594 error:
595     return luaL_error(L, "set_lastinfo error");}
596
597 /* get_lastinfo retrieves the lastinfo string from a file, whose
598    name is derived from string appid. */
599
600 static bool get_lastinfo(lua_State *L, const char *appid)
601 {
602     char path[256];
603     char result[2048];
604
605     int appid_len = strlen(appid);
606     int fd, ret, base_len;
607     char *base;
608
609     MRP_UNUSED(L);
610
611     if (!verify_appid(appid))
612         return FALSE;
613
614     if (!current_user)
615         return FALSE;
616
617     base = current_user->user_dir;
618
619     if (!base)
620         return FALSE;
621
622     base_len = strlen(base);
623
624     if (base_len + 1 + appid_len >= 256)
625         return FALSE;
626
627     strcpy(path, base);
628     path[base_len] = '/';
629     strcpy(path+base_len+1, appid);
630     path[base_len + 1 + appid_len] = '\0';
631
632     mrp_log_info("reading lastinfo from '%s'\n", path);
633
634     fd = open(path, O_RDONLY);
635
636     if (fd < 0) {
637         mrp_log_warning("failed to read lastinfo from '%s'", path);
638         return FALSE;
639     }
640
641     ret = read(fd, result, sizeof(result));
642
643     if (ret >= 0 && ret != 2048) {
644         result[ret] = '\0';
645         lua_pushstring(L, result);
646     }
647
648     close(fd);
649
650     return TRUE;
651 }
652
653 static int lua_get_lastinfo(lua_State *L)
654 {
655     const char *appid;
656     int narg;
657     user_manager_t *ctx;
658
659     narg = lua_gettop(L);
660
661     if (narg != 2)
662         return luaL_error(L, "expecting one argument");
663
664     ctx = user_manager_check(L, 1);
665
666     if (!ctx)
667         return luaL_error(L, "no self pointer");
668
669     if (!lua_isstring(L, -1))
670         goto error;
671
672     appid = lua_tostring(L, -1);
673
674     if (!appid)
675         goto error;
676
677     mrp_log_info("get_lastinfo: appid: '%s'", appid);
678
679     if (!get_lastinfo(L, appid))
680         goto error;
681
682     return 1;
683
684 error:
685     return luaL_error(L, "get_lastinfo error");
686 }
687
688 static char *create_home_dir(const char *dir_path, const char *name)
689 {
690     int dirpath_len = strlen(dir_path);
691     int name_len = strlen(name);
692     int user_dir_path_len = dirpath_len + 1 + name_len;
693     char user_dir_path[user_dir_path_len + 1];
694     struct stat buf;
695
696     mrp_debug("create user home directory: %s, %s", dir_path, name);
697
698     if (stat(dir_path, &buf) == -1) {
699
700         if (errno != ENOENT) {
701             mrp_log_error("cannot access user data directory '%s'", dir_path);
702             return NULL;
703         }
704
705         if (!mkdir_if_needed(dir_path)) {
706             mrp_log_error("could not create user data directory '%s'", dir_path);
707             return NULL;
708         }
709     }
710
711     strcpy(user_dir_path, dir_path);
712     user_dir_path[dirpath_len] = '/';
713     strcpy(user_dir_path + dirpath_len + 1, name);
714     user_dir_path[user_dir_path_len] = '\0';
715
716     if (stat(user_dir_path, &buf) == -1) {
717
718         if (errno != ENOENT) {
719             mrp_log_error("cannot access private user data directory '%s'",
720                     user_dir_path);
721             return NULL;
722         }
723
724         if (!mkdir_if_needed(user_dir_path)) {
725             mrp_log_error("could not create private user data directory '%s'",
726                     user_dir_path);
727             return NULL;
728         }
729     }
730
731     return mrp_strdup(user_dir_path);
732 }
733
734 static void delete_conf(user_config_t *conf)
735 {
736     if (!conf)
737         return;
738
739     mrp_free(conf->homescreen);
740     mrp_free(conf->name);
741     mrp_free(conf->passwd);
742     mrp_free(conf->user_dir);
743     mrp_free(conf);
744 }
745
746 static bool parse_config_file(const char *filename, char **default_user_name,
747         user_manager_config_t *ctx)
748 {
749     xmlDocPtr doc = xmlParseFile(filename);
750     xmlNodePtr root = NULL;
751     int success = FALSE;
752
753     if (!doc) {
754         mrp_log_error("Error parsing document\n");
755         exit(1);
756     }
757
758     root = xmlDocGetRootElement(doc);
759
760     if (!root) {
761         mrp_log_error("no root node in the document\n");
762         xmlFreeDoc(doc);
763         exit(1);
764     }
765
766     if (xmlStrcmp((xmlChar *) "userconfig", root->name) == 0) {
767         xmlNodePtr section = root->xmlChildrenNode;
768
769         /* can contain "user", "default", and "homescreens" */
770
771         if (!section) {
772             mrp_log_error("no section\n");
773             goto end;
774         }
775
776         do {
777             if (xmlStrcmp((xmlChar *) "users", section->name) == 0) {
778                 xmlNodePtr xusers = section->xmlChildrenNode;
779
780                 if (!xusers) {
781                     mrp_log_error("no users\n");
782                     goto end;
783                 }
784
785                 do {
786                     if (xmlStrcmp((xmlChar *) "user", xusers->name) == 0) {
787                         xmlNodePtr user_data = xusers->xmlChildrenNode;
788                         user_config_t *conf = mrp_allocz(sizeof(user_config_t));
789
790                         if (!user_data) {
791                             mrp_log_error("no user_data\n");
792                             delete_conf(conf);
793                             goto end;
794                         }
795
796                         if (!conf)
797                             goto end;
798
799                         mrp_list_init(&conf->hook);
800                         conf->ctx = ctx;
801
802                         do {
803                             if (xmlStrcmp((xmlChar *) "name", user_data->name) == 0) {
804                                 xmlChar *name;
805                                 name = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
806                                 if (!name) {
807                                     delete_conf(conf);
808                                     goto end;
809                                 }
810                                 conf->name = mrp_strdup((char *) name);
811                                 xmlFree(name);
812                             }
813                             else if (xmlStrcmp((xmlChar *) "passwd", user_data->name) == 0) {
814                                 xmlChar *passwd;
815                                 passwd = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
816                                 conf->passwd = passwd ? mrp_strdup((char *) passwd) : mrp_strdup("");
817                                 xmlFree(passwd);
818                             }
819                             else if (xmlStrcmp((xmlChar *) "hs", user_data->name) == 0) {
820                                 xmlChar *hs;
821                                 hs = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
822                                 if (!hs) {
823                                     delete_conf(conf);
824                                     goto end;
825                                 }
826                                 conf->homescreen = mrp_strdup((char *) hs);
827                                 xmlFree(hs);
828                             }
829                         } while ((user_data = user_data->next));
830
831                         /* check if we have all the data we need */
832                         if (!(conf->name && conf->passwd && conf->homescreen)) {
833                             mrp_log_error("incomplete user data");
834                             delete_conf(conf);
835                             goto end;
836                         }
837                         mrp_list_append(&users, &conf->hook);
838                     } /* if "user" */
839
840                 } while ((xusers = xusers->next));
841             } /* if "users" */
842             else if (xmlStrcmp((xmlChar *) "default", section->name) == 0) {
843                 xmlNodePtr default_user = section->xmlChildrenNode;
844
845                 if (!default_user) {
846                     mrp_log_error("no default_user\n");
847                     goto end;
848                 }
849
850                 do {
851                     if (xmlStrcmp((xmlChar *) "user", default_user->name) == 0) {
852                         xmlNodePtr user_data = default_user->xmlChildrenNode;
853
854                         if (!user_data) {
855                             mrp_log_error("no user_data\n");
856                             goto end;
857                         }
858                         do {
859                             if (xmlStrcmp((xmlChar *) "name", user_data->name) == 0) {
860                                 xmlChar *name;
861                                 name = xmlNodeListGetString(doc, user_data->xmlChildrenNode, 1);
862                                 if (!name)
863                                     goto end;
864                                 *default_user_name = mrp_strdup((char *) name);
865                                 xmlFree(name);
866                             }
867                         } while ((user_data = user_data->next));
868                     } /* if "user" */
869                 } while ((default_user = default_user->next));
870             } /*if "default" */
871         } while ((section = section->next));
872     } /* if "user_config" */
873
874     success = TRUE;
875 end:
876     xmlFreeDoc(doc);
877
878     return success;
879 }
880
881 static void user_manager_config_free(user_manager_config_t *ctx)
882 {
883     mrp_free(ctx->defaultapps_path);
884     mrp_free(ctx->flag_file_path);
885     mrp_free(ctx->user_dir_base);
886     mrp_free(ctx);
887     return;
888 }
889
890 static void find_hs(user_manager_config_t *ctx, mql_result_t *select_r,
891         mqi_event_type_t evtype)
892 {
893     int i, n_rows;
894
895     if (!select_r || select_r->type != mql_result_rows)
896         return;
897
898     n_rows = mql_result_rows_get_row_count(select_r);
899
900     for (i = 0; i < n_rows; i++) {
901         const char *appid;
902         int pid;
903
904         pid = mql_result_rows_get_integer(select_r, 0, i);
905         appid = mql_result_rows_get_string(select_r, 1, i, NULL, 0);
906
907         mrp_log_info("application %s (pid: %d) running",
908             appid ? appid : "NULL", pid);
909
910         if (!appid)
911             continue;
912
913         if (strcmp(appid, current_user->homescreen) == 0) {
914             if (evtype == mqi_row_inserted) {
915                 ctx->current_hs_pid = pid;
916                 mrp_log_info("set current homescreen pid to %d", pid);
917             }
918             else if (evtype == mqi_row_deleted) {
919                 ctx->current_hs_pid = -1;
920                 mrp_log_info("reset current homescreen pid");
921             }
922             return;
923         }
924     }
925 }
926
927 static void application_event_cb(mql_result_t *result, void *user_data)
928 {
929      user_manager_config_t *ctx = (user_manager_config_t *) user_data;
930
931      if (!current_user || !current_user->homescreen)
932         return;
933
934     if (result->type == mql_result_event) {
935
936         mqi_event_type_t evtype = mql_result_event_get_type(result);
937
938         /* If the result value is asked as a string, this is what we get:
939
940             event         table
941             -------------------------------
942             'row deleted'  aul_applications
943
944                     pid appid
945             ----------------------------------------------------------
946                     369 org.tizen.ico.onscreen
947         */
948
949         find_hs(ctx, mql_result_event_get_changed_rows(result), evtype);
950     }
951 }
952
953 static bool user_init(mrp_mainloop_t *ml, const char *filename,
954         const char *user_dir)
955 {
956     char *default_user = NULL;
957     current_user = NULL;
958     mrp_list_hook_t *p, *n;
959     int user_dir_len, ret;
960     user_manager_config_t *ctx;
961     mql_result_t *r;
962     mqi_handle_t tx;
963     const char *trigger_s = "CREATE TRIGGER row_trigger"
964             " ON ROWS IN aul_applications"
965             " CALLBACK application_event_cb"
966             " SELECT pid, appid";
967     const char *select_s = "SELECT pid, appid FROM aul_applications";
968
969     user_dir_len = strlen(user_dir);
970
971     ctx = mrp_allocz(sizeof(user_manager_config_t));
972
973     if (!ctx)
974         return FALSE;
975
976     ctx->ml = ml;
977     ctx->current_hs_pid = -1; /* unknown still */
978
979     ctx->user_dir_base = mrp_strdup(user_dir);
980     ctx->defaultapps_path =
981             mrp_allocz(user_dir_len + 1 + strlen("defaultApps.info") + 1);
982
983     if (!ctx->user_dir_base || !ctx->defaultapps_path) {
984         user_manager_config_free(ctx);
985         return FALSE;
986     }
987
988     /* TODO: do a routine for generating and validating paths, since we appear
989        to be creating a ton of them */
990
991     memcpy(ctx->defaultapps_path, user_dir, user_dir_len);
992     ctx->defaultapps_path[user_dir_len] = '/';
993     memcpy(ctx->defaultapps_path+user_dir_len+1, "defaultApps.info",
994             strlen("defaultApps.info"));
995
996     /* load the user data (username and password pairs) from a file */
997
998     mrp_log_info("user data parsing from %s", filename);
999
1000     mrp_list_init(&users);
1001
1002     if (!parse_config_file(filename, &default_user, ctx)) {
1003         mrp_free(default_user);
1004         user_manager_config_free(ctx);
1005         mrp_log_error("parsing the user.xml file failed!");
1006         return FALSE;
1007     }
1008
1009     /* create user dirs and set the default user */
1010
1011     mrp_list_foreach(&users, p, n) {
1012         user_config_t *config;
1013         config = mrp_list_entry(p, typeof(*config), hook);
1014
1015         config->user_dir = create_home_dir(user_dir, config->name);
1016
1017         if (!config->user_dir) {
1018             user_manager_config_free(ctx);
1019             return FALSE;
1020         }
1021
1022         config->runningapp_path =
1023                 mrp_allocz(strlen(config->user_dir) + 1 + strlen("runningapp.info") + 1);
1024
1025         if (!config->runningapp_path) {
1026             user_manager_config_free(ctx);
1027             return FALSE;
1028         }
1029
1030         memcpy(config->runningapp_path, config->user_dir, strlen(config->user_dir));
1031         config->runningapp_path[strlen(config->user_dir)] = '/';
1032         memcpy(config->runningapp_path+strlen(config->user_dir)+1,
1033                 "runningapp.info", strlen("runningapp.info"));
1034
1035 #if 0
1036         if (!current_user) {
1037             /* point to the first user by default */
1038             current_user = config;
1039         }
1040 #endif
1041
1042         if (default_user && strcmp(default_user, config->name) == 0)
1043             current_user = config;
1044     }
1045
1046     mrp_free(default_user);
1047
1048     /* create tmp dir for flag file */
1049
1050     if (!mkdir_if_needed("/tmp/ico")) {
1051         goto error;
1052     }
1053
1054     ctx->flag_file_path = mrp_strdup("/tmp/ico/changeUser.flag");
1055     if (!ctx->flag_file_path) {
1056         goto error;
1057     }
1058
1059     ret = mql_register_callback("application_event_cb", mql_result_event,
1060             application_event_cb, ctx);
1061
1062     if (ret < 0) {
1063         mrp_log_error("failed to register database trigger");
1064         goto error;
1065     }
1066
1067     tx = mqi_begin_transaction();
1068
1069     r = mql_exec_string(mql_result_string, trigger_s);
1070
1071    if (!mql_result_is_success(r)) {
1072         mrp_log_error("db error: %s", mql_result_error_get_message(r));
1073     }
1074
1075     mql_result_free(r);
1076
1077     mqi_commit_transaction(tx);
1078
1079     /* get the current list of running applications and see if homescreen
1080        is present */
1081
1082     tx = mqi_begin_transaction();
1083
1084     r = mql_exec_string(mql_result_rows, select_s);
1085
1086     if (!mql_result_is_success(r)) {
1087         mrp_log_error("db error: %s", mql_result_error_get_message(r));
1088
1089         /* find the home screen */
1090         find_hs(ctx, r, mqi_row_inserted);
1091
1092         mql_result_free(r);
1093     }
1094     else {
1095         mql_result_free(r);
1096     }
1097
1098     mqi_commit_transaction(tx);
1099
1100     return TRUE;
1101
1102 error:
1103     /* TODO: delete also the user list */
1104     user_manager_config_free(ctx);
1105     return FALSE;
1106 }
1107
1108 static bool user_deinit()
1109 {
1110     mrp_list_hook_t *p, *n;
1111
1112     /* TODO: stop ongoing timers */
1113
1114     /* go through users and free user configs */
1115
1116     mrp_list_foreach(&users, p, n) {
1117         user_config_t *config;
1118         config = mrp_list_entry(p, typeof(*config), hook);
1119
1120         mrp_list_delete(&config->hook);
1121
1122         delete_conf(config);
1123     }
1124
1125     /* TODO: delete the ctx */
1126
1127     return TRUE;
1128 }
1129
1130 bool mrp_user_scripting_init(lua_State *L, const char *config_file,
1131         const char *lastinfo_dir, mrp_mainloop_t *ml)
1132 {
1133     MRP_UNUSED(L);
1134
1135     return user_init(ml, config_file, lastinfo_dir);
1136 }
1137
1138 void mrp_user_scripting_deinit(lua_State *L)
1139 {
1140     MRP_UNUSED(L);
1141
1142     user_deinit();
1143 }
1144
1145 MURPHY_REGISTER_LUA_BINDINGS(murphy, USER_MANAGER_CLASS,
1146                              { "UserManager", user_manager_create });