~ubuntu-branches/ubuntu/saucy/geary/saucy-updates

« back to all changes in this revision

Viewing changes to src/engine/imap/message/imap-internal-date.vala

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher
  • Date: 2013-10-10 17:40:37 UTC
  • mfrom: (1.1.8)
  • Revision ID: package-import@ubuntu.com-20131010174037-5p5o4dlsoewek2kg
Tags: 0.4.0-0ubuntu1
New stable version

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
/**
8
8
 * A representations of IMAP's INTERNALDATE field.
9
9
 *
 
10
 * INTERNALDATE's format is
 
11
 *
 
12
 * dd-Mon-yyyy hh:mm:ss +hhmm
 
13
 *
 
14
 * Note that Mon is the standard ''English'' three-letter abbreviation.
 
15
 *
10
16
 * See [[http://tools.ietf.org/html/rfc3501#section-2.3.3]]
11
17
 */
12
18
 
13
19
public class Geary.Imap.InternalDate : Geary.MessageData.AbstractMessageData, Geary.Imap.MessageData,
14
20
    Gee.Hashable<InternalDate>, Gee.Comparable<InternalDate> {
 
21
    // see get_en_us_mon() for explanation
 
22
    private const string[] EN_US_MON = {
 
23
        "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 
24
    };
 
25
    
 
26
    private const string[] EN_US_MON_DOWN = {
 
27
        "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
 
28
    };
 
29
    
15
30
    public DateTime value { get; private set; }
16
31
    public time_t as_time_t { get; private set; }
 
32
    public string? original { get; private set; default = null; }
17
33
    
18
 
    public InternalDate(string internaldate) throws ImapError {
19
 
        as_time_t = GMime.utils_header_decode_date(internaldate, null);
20
 
        if (as_time_t == 0) {
21
 
            throw new ImapError.PARSE_ERROR("Unable to parse \"%s\": not INTERNALDATE format",
22
 
                internaldate);
23
 
        }
24
 
        
25
 
        value = new DateTime.from_unix_local(as_time_t);
 
34
    private InternalDate(string original, DateTime datetime) {
 
35
        this.original = original;
 
36
        value = datetime;
 
37
        as_time_t = Time.datetime_to_time_t(datetime);
26
38
    }
27
39
    
28
40
    public InternalDate.from_date_time(DateTime datetime) throws ImapError {
29
41
        value = datetime;
 
42
        as_time_t = Time.datetime_to_time_t(datetime);
 
43
    }
 
44
    
 
45
    public static InternalDate decode(string internaldate) throws ImapError {
 
46
        if (String.is_empty(internaldate))
 
47
            throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE: empty string");
 
48
        
 
49
        if (internaldate.length > 64)
 
50
            throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE: too long (%d)", internaldate.length);
 
51
        
 
52
        // Alas, GMime.utils_header_decode_date() is too forgiving for our needs, so do it manually
 
53
        int day, year, hour, min, sec;
 
54
        char mon[4];
 
55
        char tz[6];
 
56
        int count = internaldate.scanf("%d-%3s-%d %d:%d:%d %5s", out day, mon, out year, out hour,
 
57
            out min, out sec, tz);
 
58
        if (count != 7)
 
59
            throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": too few fields (%d)", internaldate, count);
 
60
        
 
61
        // check numerical ranges; this does not verify this is an actual date, DateTime will do
 
62
        // that (and round upward, which has to be accepted)
 
63
        if (!Numeric.int_in_range_inclusive(day, 1, 31)
 
64
            || !Numeric.int_in_range_inclusive(hour, 0, 23)
 
65
            || !Numeric.int_in_range_inclusive(min, 0, 59)
 
66
            || !Numeric.int_in_range_inclusive(sec, 0, 59)
 
67
            || year < 1970) {
 
68
            throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": bad numerical range", internaldate);
 
69
        }
 
70
        
 
71
        // check month (this catches localization problems)
 
72
        int month = -1;
 
73
        string mon_down = ((string) mon).down();
 
74
        for (int ctr = 0; ctr < EN_US_MON_DOWN.length; ctr++) {
 
75
            if (mon_down == EN_US_MON_DOWN[ctr]) {
 
76
                month = ctr;
 
77
                
 
78
                break;
 
79
            }
 
80
        }
 
81
        
 
82
        if (month < 0)
 
83
            throw new ImapError.PARSE_ERROR("Invalid INTERNALDATE \"%s\": bad month", internaldate);
 
84
        
 
85
        // TODO: verify timezone
 
86
        
 
87
        // assemble into DateTime, which validates the time as well (this is why we want to keep
 
88
        // original around, for other reasons) ... month is 1-based in DateTime
 
89
        DateTime datetime = new DateTime(new TimeZone((string) tz), year, month + 1, day, hour, min,
 
90
            sec);
 
91
        
 
92
        return new InternalDate(internaldate, datetime);
30
93
    }
31
94
    
32
95
    /**
51
114
     * @see serialize_for_search
52
115
     */
53
116
    public string serialize() {
54
 
        return value.format("%d-%b-%Y %H:%M:%S %z");
 
117
        return original ?? value.format("%d-%%s-%Y %H:%M:%S %z").printf(get_en_us_mon());
55
118
    }
56
119
    
57
120
    /**
58
 
     * Returns the {@link InternalDate}'s string representation for a SEARCH function.
 
121
     * Returns the {@link InternalDate}'s string representation for a SEARCH command.
59
122
     *
60
123
     * SEARCH does not respect time or timezone, so drop when sending it.  See
61
124
     * [[http://tools.ietf.org/html/rfc3501#section-6.4.4]]
62
125
     *
63
126
     * @see serialize
 
127
     * @see SearchCommand
64
128
     */
65
129
    public string serialize_for_search() {
66
 
        return value.format("%d-%b-%Y");
 
130
        return value.format("%d-%%s-%Y").printf(get_en_us_mon());
 
131
    }
 
132
    
 
133
    /**
 
134
     * Because IMAP's INTERNALDATE strings are ''never'' localized (as best as I can gather), so
 
135
     * need to use en_US appreviated month names, as that's the only value in INTERNALDATE that is
 
136
     * in a language and not a numeric value.
 
137
     */
 
138
    private string get_en_us_mon() {
 
139
        // month is 1-based inside of DateTime
 
140
        int mon = (value.get_month() - 1).clamp(0, EN_US_MON.length - 1);
 
141
        
 
142
        return EN_US_MON[mon];
67
143
    }
68
144
    
69
145
    public uint hash() {