~ubuntu-branches/ubuntu/trusty/unity-control-center/trusty

« back to all changes in this revision

Viewing changes to panels/user-accounts/um-utils.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2014-01-08 16:29:18 UTC
  • Revision ID: package-import@ubuntu.com-20140108162918-g29dd08tr913y2qh
Tags: upstream-14.04.0
ImportĀ upstreamĀ versionĀ 14.04.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright 2009-2010  Red Hat, Inc,
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
18
 *
 
19
 * Written by: Matthias Clasen <mclasen@redhat.com>
 
20
 */
 
21
 
 
22
#include "config.h"
 
23
 
 
24
#include <math.h>
 
25
#include <stdlib.h>
 
26
#include <sys/types.h>
 
27
#include <pwd.h>
 
28
#include <utmp.h>
 
29
 
 
30
#include <glib.h>
 
31
#include <glib/gi18n.h>
 
32
 
 
33
#include "um-utils.h"
 
34
 
 
35
typedef struct {
 
36
        gchar *text;
 
37
        gchar *placeholder_str;
 
38
        GIcon *icon;
 
39
        gunichar placeholder;
 
40
        gulong query_id;
 
41
} IconShapeData;
 
42
 
 
43
static IconShapeData *
 
44
icon_shape_data_new (const gchar *text,
 
45
                     const gchar *placeholder,
 
46
                     GIcon       *icon)
 
47
{
 
48
        IconShapeData *data;
 
49
 
 
50
        data = g_new0 (IconShapeData, 1);
 
51
 
 
52
        data->text = g_strdup (text);
 
53
        data->placeholder_str = g_strdup (placeholder);
 
54
        data->placeholder = g_utf8_get_char_validated (placeholder, -1);
 
55
        data->icon = g_object_ref (icon);
 
56
 
 
57
        return data;
 
58
}
 
59
 
 
60
static void
 
61
icon_shape_data_free (gpointer user_data)
 
62
{
 
63
        IconShapeData *data = user_data;
 
64
 
 
65
        g_free (data->text);
 
66
        g_free (data->placeholder_str);
 
67
        g_object_unref (data->icon);
 
68
        g_free (data);
 
69
}
 
70
 
 
71
static void
 
72
icon_shape_renderer (cairo_t        *cr,
 
73
                     PangoAttrShape *attr,
 
74
                     gboolean        do_path,
 
75
                     gpointer        user_data)
 
76
{
 
77
        IconShapeData *data = user_data;
 
78
        gdouble x, y;
 
79
 
 
80
        cairo_get_current_point (cr, &x, &y);
 
81
        if (GPOINTER_TO_UINT (attr->data) == data->placeholder) {
 
82
                gdouble ascent;
 
83
                gdouble height;
 
84
                GdkPixbuf *pixbuf;
 
85
                GtkIconInfo *info;
 
86
 
 
87
                ascent = pango_units_to_double (attr->ink_rect.y);
 
88
                height = pango_units_to_double (attr->ink_rect.height);
 
89
                info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (),
 
90
                                                       data->icon,
 
91
                                                       (gint)height,
 
92
                                                       GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_USE_BUILTIN);
 
93
                pixbuf = gtk_icon_info_load_icon (info, NULL);
 
94
                gtk_icon_info_free (info);
 
95
 
 
96
                cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
 
97
                cairo_reset_clip (cr);
 
98
                gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y + ascent);
 
99
                cairo_paint (cr);
 
100
                g_object_unref (pixbuf);
 
101
        }
 
102
}
 
103
 
 
104
static PangoAttrList *
 
105
create_shape_attr_list_for_layout (PangoLayout   *layout,
 
106
                                   IconShapeData *data)
 
107
{
 
108
        PangoAttrList *attrs;
 
109
        PangoFontMetrics *metrics;
 
110
        gint ascent, descent;
 
111
        PangoRectangle ink_rect, logical_rect;
 
112
        const gchar *p;
 
113
        const gchar *text;
 
114
        gint placeholder_len;
 
115
 
 
116
        /* Get font metrics and prepare fancy shape size */
 
117
        metrics = pango_context_get_metrics (pango_layout_get_context (layout),
 
118
                                             pango_layout_get_font_description (layout),
 
119
                                             NULL);
 
120
        ascent = pango_font_metrics_get_ascent (metrics);
 
121
        descent = pango_font_metrics_get_descent (metrics);
 
122
        pango_font_metrics_unref (metrics);
 
123
 
 
124
        logical_rect.x = 0;
 
125
        logical_rect.y = - ascent;
 
126
        logical_rect.width = ascent + descent;
 
127
        logical_rect.height = ascent + descent;
 
128
 
 
129
        ink_rect = logical_rect;
 
130
 
 
131
        attrs = pango_attr_list_new ();
 
132
        text = pango_layout_get_text (layout);
 
133
        placeholder_len = strlen (data->placeholder_str);
 
134
        for (p = text; (p = strstr (p, data->placeholder_str)); p += placeholder_len) {
 
135
                PangoAttribute *attr;
 
136
 
 
137
                attr = pango_attr_shape_new_with_data (&ink_rect,
 
138
                                                       &logical_rect,
 
139
                                                       GUINT_TO_POINTER (g_utf8_get_char (p)),
 
140
                                                       NULL, NULL);
 
141
 
 
142
                attr->start_index = p - text;
 
143
                attr->end_index = attr->start_index + placeholder_len;
 
144
 
 
145
                pango_attr_list_insert (attrs, attr);
 
146
        }
 
147
 
 
148
        return attrs;
 
149
}
 
150
 
 
151
static gboolean
 
152
query_unlock_tooltip (GtkWidget  *widget,
 
153
                      gint        x,
 
154
                      gint        y,
 
155
                      gboolean    keyboard_tooltip,
 
156
                      GtkTooltip *tooltip,
 
157
                      gpointer    user_data)
 
158
{
 
159
        GtkWidget *label;
 
160
        PangoLayout *layout;
 
161
        PangoAttrList *attrs;
 
162
        IconShapeData *data;
 
163
 
 
164
        data = g_object_get_data (G_OBJECT (widget), "icon-shape-data");
 
165
        label = g_object_get_data (G_OBJECT (widget), "tooltip-label");
 
166
        if (label == NULL) {
 
167
                label = gtk_label_new (data->text);
 
168
                g_object_ref_sink (label);
 
169
                g_object_set_data_full (G_OBJECT (widget),
 
170
                                        "tooltip-label", label, g_object_unref);
 
171
        }
 
172
 
 
173
        layout = gtk_label_get_layout (GTK_LABEL (label));
 
174
        pango_cairo_context_set_shape_renderer (pango_layout_get_context (layout),
 
175
                                                icon_shape_renderer,
 
176
                                                data, NULL);
 
177
 
 
178
        attrs = create_shape_attr_list_for_layout (layout, data);
 
179
        gtk_label_set_attributes (GTK_LABEL (label), attrs);
 
180
        pango_attr_list_unref (attrs);
 
181
 
 
182
        gtk_tooltip_set_custom (tooltip, label);
 
183
 
 
184
        return TRUE;
 
185
}
 
186
 
 
187
void
 
188
setup_tooltip_with_embedded_icon (GtkWidget   *widget,
 
189
                                  const gchar *text,
 
190
                                  const gchar *placeholder,
 
191
                                  GIcon       *icon)
 
192
{
 
193
        IconShapeData *data;
 
194
 
 
195
        data = g_object_get_data (G_OBJECT (widget), "icon-shape-data");
 
196
        if (data) {
 
197
                gtk_widget_set_has_tooltip (widget, FALSE);
 
198
                g_signal_handler_disconnect (widget, data->query_id);
 
199
                g_object_set_data (G_OBJECT (widget), "icon-shape-data", NULL);
 
200
                g_object_set_data (G_OBJECT (widget), "tooltip-label", NULL);
 
201
        }
 
202
 
 
203
        if (!placeholder) {
 
204
                gtk_widget_set_tooltip_text (widget, text);
 
205
                return;
 
206
        }
 
207
 
 
208
        data = icon_shape_data_new (text, placeholder, icon);
 
209
        g_object_set_data_full (G_OBJECT (widget),
 
210
                                "icon-shape-data",
 
211
                                data,
 
212
                                icon_shape_data_free);
 
213
 
 
214
        gtk_widget_set_has_tooltip (widget, TRUE);
 
215
        data->query_id = g_signal_connect (widget, "query-tooltip",
 
216
                                           G_CALLBACK (query_unlock_tooltip), NULL);
 
217
 
 
218
}
 
219
 
 
220
gboolean
 
221
show_tooltip_now (GtkWidget *widget,
 
222
                  GdkEvent  *event)
 
223
{
 
224
        GtkSettings *settings;
 
225
        gint timeout;
 
226
 
 
227
        settings = gtk_widget_get_settings (widget);
 
228
 
 
229
        g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
 
230
        g_object_set (settings, "gtk-tooltip-timeout", 1, NULL);
 
231
        gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (widget));
 
232
        g_object_set (settings, "gtk-tooltip-timeout", timeout, NULL);
 
233
 
 
234
        return FALSE;
 
235
}
 
236
 
 
237
static gboolean
 
238
query_tooltip (GtkWidget  *widget,
 
239
               gint        x,
 
240
               gint        y,
 
241
               gboolean    keyboard_mode,
 
242
               GtkTooltip *tooltip,
 
243
               gpointer    user_data)
 
244
{
 
245
        gchar *tip;
 
246
 
 
247
        if (GTK_ENTRY_ICON_SECONDARY == gtk_entry_get_icon_at_pos (GTK_ENTRY (widget), x, y)) {
 
248
                tip = gtk_entry_get_icon_tooltip_text (GTK_ENTRY (widget),
 
249
                                                       GTK_ENTRY_ICON_SECONDARY);
 
250
                gtk_tooltip_set_text (tooltip, tip);
 
251
                g_free (tip);
 
252
 
 
253
                return TRUE;
 
254
        }
 
255
        else {
 
256
                return FALSE;
 
257
        }
 
258
}
 
259
 
 
260
static void
 
261
icon_released (GtkEntry             *entry,
 
262
              GtkEntryIconPosition  pos,
 
263
              GdkEvent             *event,
 
264
              gpointer              user_data)
 
265
{
 
266
        GtkSettings *settings;
 
267
        gint timeout;
 
268
 
 
269
        settings = gtk_widget_get_settings (GTK_WIDGET (entry));
 
270
 
 
271
        g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
 
272
        g_object_set (settings, "gtk-tooltip-timeout", 1, NULL);
 
273
        gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (GTK_WIDGET (entry)));
 
274
        g_object_set (settings, "gtk-tooltip-timeout", timeout, NULL);
 
275
}
 
276
 
 
277
 
 
278
void
 
279
set_entry_validation_error (GtkEntry    *entry,
 
280
                            const gchar *text)
 
281
{
 
282
        g_object_set (entry, "caps-lock-warning", FALSE, NULL);
 
283
        gtk_entry_set_icon_from_icon_name (entry,
 
284
                                           GTK_ENTRY_ICON_SECONDARY,
 
285
                                           "dialog-error-symbolic");
 
286
        gtk_entry_set_icon_activatable (entry,
 
287
                                        GTK_ENTRY_ICON_SECONDARY,
 
288
                                        TRUE);
 
289
        g_signal_connect (entry, "icon-release",
 
290
                          G_CALLBACK (icon_released), FALSE);
 
291
        g_signal_connect (entry, "query-tooltip",
 
292
                          G_CALLBACK (query_tooltip), NULL);
 
293
        g_object_set (entry, "has-tooltip", TRUE, NULL);
 
294
        gtk_entry_set_icon_tooltip_text (entry,
 
295
                                         GTK_ENTRY_ICON_SECONDARY,
 
296
                                         text);
 
297
}
 
298
 
 
299
void
 
300
clear_entry_validation_error (GtkEntry *entry)
 
301
{
 
302
        gboolean warning;
 
303
 
 
304
        g_object_get (entry, "caps-lock-warning", &warning, NULL);
 
305
 
 
306
        if (warning)
 
307
                return;
 
308
 
 
309
        g_object_set (entry, "has-tooltip", FALSE, NULL);
 
310
        gtk_entry_set_icon_from_pixbuf (entry,
 
311
                                        GTK_ENTRY_ICON_SECONDARY,
 
312
                                        NULL);
 
313
        g_object_set (entry, "caps-lock-warning", TRUE, NULL);
 
314
}
 
315
 
 
316
void
 
317
popup_menu_below_button (GtkMenu   *menu,
 
318
                         gint      *x,
 
319
                         gint      *y,
 
320
                         gboolean  *push_in,
 
321
                         GtkWidget *button)
 
322
{
 
323
        GtkRequisition menu_req;
 
324
        GtkTextDirection direction;
 
325
        GtkAllocation allocation;
 
326
 
 
327
        gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &menu_req);
 
328
 
 
329
        direction = gtk_widget_get_direction (button);
 
330
 
 
331
        gdk_window_get_origin (gtk_widget_get_window (button), x, y);
 
332
        gtk_widget_get_allocation (button, &allocation);
 
333
        *x += allocation.x;
 
334
        *y += allocation.y + allocation.height;
 
335
 
 
336
        if (direction == GTK_TEXT_DIR_LTR)
 
337
                *x += MAX (allocation.width - menu_req.width, 0);
 
338
        else if (menu_req.width > allocation.width)
 
339
                *x -= menu_req.width - allocation.width;
 
340
 
 
341
        *push_in = TRUE;
 
342
}
 
343
 
 
344
void
 
345
rounded_rectangle (cairo_t *cr,
 
346
                   gdouble  aspect,
 
347
                   gdouble  x,
 
348
                   gdouble  y,
 
349
                   gdouble  corner_radius,
 
350
                   gdouble  width,
 
351
                   gdouble  height)
 
352
{
 
353
        gdouble radius;
 
354
        gdouble degrees;
 
355
 
 
356
        radius = corner_radius / aspect;
 
357
        degrees = G_PI / 180.0;
 
358
 
 
359
        cairo_new_sub_path (cr);
 
360
        cairo_arc (cr,
 
361
                   x + width - radius,
 
362
                   y + radius,
 
363
                   radius,
 
364
                   -90 * degrees,
 
365
                   0 * degrees);
 
366
        cairo_arc (cr,
 
367
                   x + width - radius,
 
368
                   y + height - radius,
 
369
                   radius,
 
370
                   0 * degrees,
 
371
                   90 * degrees);
 
372
        cairo_arc (cr,
 
373
                   x + radius,
 
374
                   y + height - radius,
 
375
                   radius,
 
376
                   90 * degrees,
 
377
                   180 * degrees);
 
378
        cairo_arc (cr,
 
379
                   x + radius,
 
380
                   y + radius,
 
381
                   radius,
 
382
                   180 * degrees,
 
383
                   270 * degrees);
 
384
        cairo_close_path (cr);
 
385
}
 
386
 
 
387
void
 
388
down_arrow (GtkStyleContext *context,
 
389
            cairo_t         *cr,
 
390
            gint             x,
 
391
            gint             y,
 
392
            gint             width,
 
393
            gint             height)
 
394
{
 
395
        GtkStateFlags flags;
 
396
        GdkRGBA fg_color;
 
397
        GdkRGBA outline_color;
 
398
        gdouble vertical_overshoot;
 
399
        gint diameter;
 
400
        gdouble radius;
 
401
        gdouble x_double, y_double;
 
402
        gdouble angle;
 
403
        gint line_width;
 
404
 
 
405
        flags = gtk_style_context_get_state (context);
 
406
 
 
407
        gtk_style_context_get_color (context, flags, &fg_color);
 
408
        gtk_style_context_get_border_color (context, flags, &outline_color);
 
409
 
 
410
        line_width = 1;
 
411
        angle = G_PI / 2;
 
412
        vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
 
413
        if (line_width % 2 == 1)
 
414
                vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
 
415
        else
 
416
                vertical_overshoot = ceil (vertical_overshoot);
 
417
        diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
 
418
        diameter -= (1 - (diameter + line_width) % 2);
 
419
        radius = diameter / 2.;
 
420
        x_double = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
 
421
 
 
422
        y_double = (y + height / 2) - 0.5;
 
423
 
 
424
        cairo_save (cr);
 
425
 
 
426
        cairo_translate (cr, x_double, y_double);
 
427
        cairo_rotate (cr, angle);
 
428
 
 
429
        cairo_move_to (cr, - radius / 2., - radius);
 
430
        cairo_line_to (cr,   radius / 2.,   0);
 
431
        cairo_line_to (cr, - radius / 2.,   radius);
 
432
 
 
433
        cairo_close_path (cr);
 
434
 
 
435
        cairo_set_line_width (cr, line_width);
 
436
 
 
437
        gdk_cairo_set_source_rgba (cr, &fg_color);
 
438
 
 
439
        cairo_fill_preserve (cr);
 
440
 
 
441
        gdk_cairo_set_source_rgba (cr, &outline_color);
 
442
        cairo_stroke (cr);
 
443
 
 
444
        cairo_restore (cr);
 
445
}
 
446
 
 
447
 
 
448
#define MAXNAMELEN  (UT_NAMESIZE - 1)
 
449
 
 
450
static gboolean
 
451
is_username_used (const gchar *username)
 
452
{
 
453
        struct passwd *pwent;
 
454
 
 
455
        if (username == NULL || username[0] == '\0') {
 
456
                return FALSE;
 
457
        }
 
458
 
 
459
        pwent = getpwnam (username);
 
460
 
 
461
        return pwent != NULL;
 
462
}
 
463
 
 
464
gboolean
 
465
is_valid_name (const gchar *name)
 
466
{
 
467
        gboolean valid;
 
468
 
 
469
        valid = (strlen (name) > 0);
 
470
 
 
471
        return valid;
 
472
}
 
473
 
 
474
gboolean
 
475
is_valid_username (const gchar *username, gchar **tip)
 
476
{
 
477
        gboolean empty;
 
478
        gboolean in_use;
 
479
        gboolean too_long;
 
480
        gboolean valid;
 
481
        const gchar *c;
 
482
 
 
483
        if (username == NULL || username[0] == '\0') {
 
484
                empty = TRUE;
 
485
                in_use = FALSE;
 
486
                too_long = FALSE;
 
487
        } else {
 
488
                empty = FALSE;
 
489
                in_use = is_username_used (username);
 
490
                too_long = strlen (username) > MAXNAMELEN;
 
491
        }
 
492
        valid = TRUE;
 
493
 
 
494
        if (!in_use && !empty && !too_long) {
 
495
                /* First char must be a letter, and it must only composed
 
496
                 * of ASCII letters, digits, and a '.', '-', '_'
 
497
                 */
 
498
                for (c = username; *c; c++) {
 
499
                        if (! ((*c >= 'a' && *c <= 'z') ||
 
500
                               (*c >= 'A' && *c <= 'Z') ||
 
501
                               (*c >= '0' && *c <= '9') ||
 
502
                               (*c == '_') || (*c == '.') ||
 
503
                               (*c == '-' && c != username)))
 
504
                           valid = FALSE;
 
505
                }
 
506
        }
 
507
 
 
508
        valid = !empty && !in_use && !too_long && valid;
 
509
 
 
510
        if (!empty && (in_use || too_long || !valid)) {
 
511
                if (in_use) {
 
512
                        *tip = g_strdup_printf (_("A user with the username '%s' already exists"),
 
513
                                               username);
 
514
                }
 
515
                else if (too_long) {
 
516
                        *tip = g_strdup_printf (_("The username is too long"));
 
517
                }
 
518
                else if (username[0] == '-') {
 
519
                        *tip = g_strdup (_("The username cannot start with a '-'"));
 
520
                }
 
521
                else {
 
522
                        *tip = g_strdup (_("The username must only consist of:\n"
 
523
                                          " \xe2\x9e\xa3 letters from the English alphabet\n"
 
524
                                          " \xe2\x9e\xa3 digits\n"
 
525
                                          " \xe2\x9e\xa3 any of the characters '.', '-' and '_'"));
 
526
                }
 
527
        }
 
528
        else {
 
529
                *tip = NULL;
 
530
        }
 
531
 
 
532
        return valid;
 
533
}
 
534
 
 
535
void
 
536
generate_username_choices (const gchar  *name,
 
537
                           GtkListStore *store)
 
538
{
 
539
        gboolean in_use;
 
540
        char *lc_name, *ascii_name, *stripped_name;
 
541
        char **words1;
 
542
        char **words2 = NULL;
 
543
        char **w1, **w2;
 
544
        char *c;
 
545
        char *unicode_fallback = "?";
 
546
        GString *first_word, *last_word;
 
547
        GString *item0, *item1, *item2, *item3, *item4;
 
548
        int len;
 
549
        int nwords1, nwords2, i;
 
550
        GHashTable *items;
 
551
        GtkTreeIter iter;
 
552
 
 
553
        gtk_list_store_clear (store);
 
554
 
 
555
        ascii_name = g_convert_with_fallback (name, -1, "ASCII//TRANSLIT", "UTF-8",
 
556
                                              unicode_fallback, NULL, NULL, NULL);
 
557
 
 
558
        lc_name = g_ascii_strdown (ascii_name, -1);
 
559
 
 
560
        /* Remove all non ASCII alphanumeric chars from the name,
 
561
         * apart from the few allowed symbols.
 
562
         *
 
563
         * We do remove '.', even though it is usually allowed,
 
564
         * since it often comes in via an abbreviated middle name,
 
565
         * and the dot looks just wrong in the proposals then.
 
566
         */
 
567
        stripped_name = g_strnfill (strlen (lc_name) + 1, '\0');
 
568
        i = 0;
 
569
        for (c = lc_name; *c; c++) {
 
570
                if (!(g_ascii_isdigit (*c) || g_ascii_islower (*c) ||
 
571
                    *c == ' ' || *c == '-' || *c == '_' ||
 
572
                    /* used to track invalid words, removed below */
 
573
                    *c == '?') )
 
574
                        continue;
 
575
 
 
576
                    stripped_name[i] = *c;
 
577
                    i++;
 
578
        }
 
579
 
 
580
        if (strlen (stripped_name) == 0) {
 
581
                g_free (ascii_name);
 
582
                g_free (lc_name);
 
583
                g_free (stripped_name);
 
584
                return;
 
585
        }
 
586
 
 
587
        /* we split name on spaces, and then on dashes, so that we can treat
 
588
         * words linked with dashes the same way, i.e. both fully shown, or
 
589
         * both abbreviated
 
590
         */
 
591
        words1 = g_strsplit_set (stripped_name, " ", -1);
 
592
        len = g_strv_length (words1);
 
593
 
 
594
        /* The default item is a concatenation of all words without ? */
 
595
        item0 = g_string_sized_new (strlen (stripped_name));
 
596
 
 
597
        g_free (ascii_name);
 
598
        g_free (lc_name);
 
599
        g_free (stripped_name);
 
600
 
 
601
        /* Concatenate the whole first word with the first letter of each
 
602
         * word (item1), and the last word with the first letter of each
 
603
         * word (item2). item3 and item4 are symmetrical respectively to
 
604
         * item1 and item2.
 
605
         *
 
606
         * Constant 5 is the max reasonable number of words we may get when
 
607
         * splitting on dashes, since we can't guess it at this point,
 
608
         * and reallocating would be too bad.
 
609
         */
 
610
        item1 = g_string_sized_new (strlen (words1[0]) + len - 1 + 5);
 
611
        item3 = g_string_sized_new (strlen (words1[0]) + len - 1 + 5);
 
612
 
 
613
        item2 = g_string_sized_new (strlen (words1[len - 1]) + len - 1 + 5);
 
614
        item4 = g_string_sized_new (strlen (words1[len - 1]) + len - 1 + 5);
 
615
 
 
616
        /* again, guess at the max size of names */
 
617
        first_word = g_string_sized_new (20);
 
618
        last_word = g_string_sized_new (20);
 
619
 
 
620
        nwords1 = 0;
 
621
        nwords2 = 0;
 
622
        for (w1 = words1; *w1; w1++) {
 
623
                if (strlen (*w1) == 0)
 
624
                        continue;
 
625
 
 
626
                /* skip words with string '?', most likely resulting
 
627
                 * from failed transliteration to ASCII
 
628
                 */
 
629
                if (strstr (*w1, unicode_fallback) != NULL)
 
630
                        continue;
 
631
 
 
632
                nwords1++; /* count real words, excluding empty string */
 
633
 
 
634
                item0 = g_string_append (item0, *w1);
 
635
 
 
636
                words2 = g_strsplit_set (*w1, "-", -1);
 
637
                /* reset last word if a new non-empty word has been found */
 
638
                if (strlen (*words2) > 0)
 
639
                        last_word = g_string_set_size (last_word, 0);
 
640
 
 
641
                for (w2 = words2; *w2; w2++) {
 
642
                        if (strlen (*w2) == 0)
 
643
                                continue;
 
644
 
 
645
                        nwords2++;
 
646
 
 
647
                        /* part of the first "toplevel" real word */
 
648
                        if (nwords1 == 1) {
 
649
                                item1 = g_string_append (item1, *w2);
 
650
                                first_word = g_string_append (first_word, *w2);
 
651
                        }
 
652
                        else {
 
653
                                item1 = g_string_append_unichar (item1,
 
654
                                                                 g_utf8_get_char (*w2));
 
655
                                item3 = g_string_append_unichar (item3,
 
656
                                                                 g_utf8_get_char (*w2));
 
657
                        }
 
658
 
 
659
                        /* not part of the last "toplevel" word */
 
660
                        if (w1 != words1 + len - 1) {
 
661
                                item2 = g_string_append_unichar (item2,
 
662
                                                                 g_utf8_get_char (*w2));
 
663
                                item4 = g_string_append_unichar (item4,
 
664
                                                                 g_utf8_get_char (*w2));
 
665
                        }
 
666
 
 
667
                        /* always save current word so that we have it if last one reveals empty */
 
668
                        last_word = g_string_append (last_word, *w2);
 
669
                }
 
670
 
 
671
                g_strfreev (words2);
 
672
        }
 
673
        item2 = g_string_append (item2, last_word->str);
 
674
        item3 = g_string_append (item3, first_word->str);
 
675
        item4 = g_string_prepend (item4, last_word->str);
 
676
 
 
677
        items = g_hash_table_new (g_str_hash, g_str_equal);
 
678
 
 
679
        in_use = is_username_used (item0->str);
 
680
        if (!in_use && !g_ascii_isdigit (item0->str[0])) {
 
681
                gtk_list_store_append (store, &iter);
 
682
                gtk_list_store_set (store, &iter, 0, item0->str, -1);
 
683
                g_hash_table_insert (items, item0->str, item0->str);
 
684
        }
 
685
 
 
686
        in_use = is_username_used (item1->str);
 
687
        if (nwords2 > 0 && !in_use && !g_ascii_isdigit (item1->str[0])) {
 
688
                gtk_list_store_append (store, &iter);
 
689
                gtk_list_store_set (store, &iter, 0, item1->str, -1);
 
690
                g_hash_table_insert (items, item1->str, item1->str);
 
691
        }
 
692
 
 
693
        /* if there's only one word, would be the same as item1 */
 
694
        if (nwords2 > 1) {
 
695
                /* add other items */
 
696
                in_use = is_username_used (item2->str);
 
697
                if (!in_use && !g_ascii_isdigit (item2->str[0]) &&
 
698
                    !g_hash_table_lookup (items, item2->str)) {
 
699
                        gtk_list_store_append (store, &iter);
 
700
                        gtk_list_store_set (store, &iter, 0, item2->str, -1);
 
701
                        g_hash_table_insert (items, item2->str, item2->str);
 
702
                }
 
703
 
 
704
                in_use = is_username_used (item3->str);
 
705
                if (!in_use && !g_ascii_isdigit (item3->str[0]) &&
 
706
                    !g_hash_table_lookup (items, item3->str)) {
 
707
                        gtk_list_store_append (store, &iter);
 
708
                        gtk_list_store_set (store, &iter, 0, item3->str, -1);
 
709
                        g_hash_table_insert (items, item3->str, item3->str);
 
710
                }
 
711
 
 
712
                in_use = is_username_used (item4->str);
 
713
                if (!in_use && !g_ascii_isdigit (item4->str[0]) &&
 
714
                    !g_hash_table_lookup (items, item4->str)) {
 
715
                        gtk_list_store_append (store, &iter);
 
716
                        gtk_list_store_set (store, &iter, 0, item4->str, -1);
 
717
                        g_hash_table_insert (items, item4->str, item4->str);
 
718
                }
 
719
 
 
720
                /* add the last word */
 
721
                in_use = is_username_used (last_word->str);
 
722
                if (!in_use && !g_ascii_isdigit (last_word->str[0]) &&
 
723
                    !g_hash_table_lookup (items, last_word->str)) {
 
724
                        gtk_list_store_append (store, &iter);
 
725
                        gtk_list_store_set (store, &iter, 0, last_word->str, -1);
 
726
                        g_hash_table_insert (items, last_word->str, last_word->str);
 
727
                }
 
728
 
 
729
                /* ...and the first one */
 
730
                in_use = is_username_used (first_word->str);
 
731
                if (!in_use && !g_ascii_isdigit (first_word->str[0]) &&
 
732
                    !g_hash_table_lookup (items, first_word->str)) {
 
733
                        gtk_list_store_append (store, &iter);
 
734
                        gtk_list_store_set (store, &iter, 0, first_word->str, -1);
 
735
                        g_hash_table_insert (items, first_word->str, first_word->str);
 
736
                }
 
737
        }
 
738
 
 
739
        g_hash_table_destroy (items);
 
740
        g_strfreev (words1);
 
741
        g_string_free (first_word, TRUE);
 
742
        g_string_free (last_word, TRUE);
 
743
        g_string_free (item0, TRUE);
 
744
        g_string_free (item1, TRUE);
 
745
        g_string_free (item2, TRUE);
 
746
        g_string_free (item3, TRUE);
 
747
        g_string_free (item4, TRUE);
 
748
}
 
749
 
 
750
gboolean
 
751
is_using_ecryptfs (const gchar *name)
 
752
{
 
753
        gboolean using_ecryptfs = FALSE;
 
754
        int status;
 
755
        gchar *prog;
 
756
        gchar *cmd;
 
757
 
 
758
        prog = g_find_program_in_path ("ecryptfs-verify");
 
759
        if (prog != NULL) {
 
760
                gchar *cmd = g_strdup_printf("'%s' -h -u '%s'", prog, name);
 
761
 
 
762
                if (g_spawn_command_line_sync (cmd, NULL, NULL, &status, NULL)) {
 
763
                        if (status == 0)
 
764
                                using_ecryptfs = TRUE;
 
765
                }
 
766
 
 
767
                g_free (prog);
 
768
                g_free (cmd);
 
769
        }
 
770
 
 
771
        return using_ecryptfs;
 
772
}