• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GothenburgBitFactory / taskwarrior / 11489652313

23 Oct 2024 11:18PM UTC coverage: 84.827% (-0.007%) from 84.834%
11489652313

push

github

web-flow
Limit the allowed epoch timestamps (#3651)

The code for parsing epoch timestamps when displaying tasks only
supports values between year 1980 and 9999. Previous to this change, it
was possible to set e.g., the due timestamp to a value outside of these
limits, which would make it impossible to later on show the task.

With this change, we only allow setting values within the same limits
used by the code for displaying tasks.

3 of 4 new or added lines in 1 file covered. (75.0%)

1 existing line in 1 file now uncovered.

19025 of 22428 relevant lines covered (84.83%)

23055.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.11
/src/columns/ColTypeDate.cpp
1
////////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2006 - 2021, Tomas Babej, Paul Beckingham, Federico Hernandez.
4
//
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
6
// of this software and associated documentation files (the "Software"), to deal
7
// in the Software without restriction, including without limitation the rights
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the Software is
10
// furnished to do so, subject to the following conditions:
11
//
12
// The above copyright notice and this permission notice shall be included
13
// in all copies or substantial portions of the Software.
14
//
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
// SOFTWARE.
22
//
23
// https://www.opensource.org/licenses/mit-license.php
24
//
25
////////////////////////////////////////////////////////////////////////////////
26

27
#include <cmake.h>
28
// cmake.h include header must come first
29

30
#include <ColTypeDate.h>
31
#include <Context.h>
32
#include <Datetime.h>
33
#include <Duration.h>
34
#include <Eval.h>
35
#include <Filter.h>
36
#include <Variant.h>
37
#include <format.h>
38

39
////////////////////////////////////////////////////////////////////////////////
40
ColumnTypeDate::ColumnTypeDate() {
39,085✔
41
  _name = "";
39,085✔
42
  _type = "date";
39,085✔
43
  _style = "formatted";
39,085✔
44
  _label = "";
39,085✔
45
  _styles = {"formatted", "julian", "epoch", "iso", "age", "relative", "remaining", "countdown"};
351,765✔
46

47
  Datetime now;
39,085✔
48
  now -= 125;  // So that "age" is non-zero.
39,085✔
49
  _examples = {now.toString(Context::getContext().config.get("dateformat")),
547,190✔
50
               format(now.toJulian(), 13, 12),
51
               now.toEpochString(),
52
               now.toISO(),
53
               Duration(Datetime() - now).formatVague(true),
39,085✔
54
               '-' + Duration(Datetime() - now).formatVague(true),
78,170✔
55
               "",
56
               Duration(Datetime() - now).format()};
429,935✔
57
}
39,085✔
58

59
////////////////////////////////////////////////////////////////////////////////
60
// Set the minimum and maximum widths for the value.
61
void ColumnTypeDate::measure(Task& task, unsigned int& minimum, unsigned int& maximum) {
4,796✔
62
  minimum = maximum = 0;
4,796✔
63
  if (task.has(_name)) {
4,796✔
64
    Datetime date(task.get_date(_name));
1,356✔
65

66
    if (_style == "default" || _style == "formatted") {
1,356✔
67
      // Determine the output date format, which uses a hierarchy of definitions.
68
      //   rc.report.<report>.dateformat
69
      //   rc.dateformat.report
70
      //   rc.dateformat.
71
      std::string format = Context::getContext().config.get("report." + _report + ".dateformat");
724✔
72
      if (format == "") format = Context::getContext().config.get("dateformat.report");
362✔
73
      if (format == "") format = Context::getContext().config.get("dateformat");
362✔
74

75
      minimum = maximum = Datetime::length(format);
362✔
76
    } else if (_style == "countdown") {
1,356✔
77
      Datetime now;
84✔
78
      minimum = maximum = Duration(date - now).formatVague(true).length();
84✔
79
    } else if (_style == "julian") {
910✔
80
      minimum = maximum = format(date.toJulian(), 13, 12).length();
2✔
81
    } else if (_style == "epoch") {
908✔
82
      minimum = maximum = date.toEpochString().length();
2✔
83
    } else if (_style == "iso") {
906✔
84
      minimum = maximum = date.toISO().length();
2✔
85
    } else if (_style == "age") {
904✔
86
      Datetime now;
847✔
87
      if (now > date)
847✔
88
        minimum = maximum = Duration(now - date).formatVague(true).length();
444✔
89
      else
90
        minimum = maximum = Duration(date - now).formatVague(true).length() + 1;
403✔
91
    } else if (_style == "relative") {
57✔
92
      Datetime now;
19✔
93
      if (now < date)
19✔
94
        minimum = maximum = Duration(date - now).formatVague(true).length();
9✔
95
      else
96
        minimum = maximum = Duration(now - date).formatVague(true).length() + 1;
10✔
97
    } else if (_style == "remaining") {
38✔
98
      Datetime now;
38✔
99
      if (date > now) minimum = maximum = Duration(date - now).formatVague(true).length();
38✔
100
    }
101
  }
102
}
4,796✔
103

104
////////////////////////////////////////////////////////////////////////////////
105
void ColumnTypeDate::render(std::vector<std::string>& lines, Task& task, int width, Color& color) {
1,749✔
106
  if (task.has(_name)) {
1,749✔
107
    Datetime date(task.get_date(_name));
1,337✔
108

109
    if (_style == "default" || _style == "formatted") {
1,337✔
110
      // Determine the output date format, which uses a hierarchy of definitions.
111
      //   rc.report.<report>.dateformat
112
      //   rc.dateformat.report
113
      //   rc.dateformat
114
      std::string format = Context::getContext().config.get("report." + _report + ".dateformat");
724✔
115
      if (format == "") {
362✔
116
        format = Context::getContext().config.get("dateformat.report");
343✔
117
        if (format == "") format = Context::getContext().config.get("dateformat");
343✔
118
      }
119

120
      renderStringLeft(lines, width, color, date.toString(format));
362✔
121
    } else if (_style == "countdown") {
1,337✔
122
      Datetime now;
68✔
123
      renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
68✔
124
    } else if (_style == "julian")
907✔
125
      renderStringRight(lines, width, color, format(date.toJulian(), 13, 12));
2✔
126

127
    else if (_style == "epoch")
905✔
128
      renderStringRight(lines, width, color, date.toEpochString());
2✔
129

130
    else if (_style == "iso")
903✔
131
      renderStringLeft(lines, width, color, date.toISO());
2✔
132

133
    else if (_style == "age") {
901✔
134
      Datetime now;
847✔
135
      if (now > date)
847✔
136
        renderStringRight(lines, width, color, Duration(now - date).formatVague(true));
444✔
137
      else
138
        renderStringRight(lines, width, color, '-' + Duration(date - now).formatVague(true));
403✔
139
    } else if (_style == "relative") {
54✔
140
      Datetime now;
19✔
141
      if (now < date)
19✔
142
        renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
9✔
143
      else
144
        renderStringRight(lines, width, color, '-' + Duration(now - date).formatVague(true));
10✔
145
    }
146

147
    else if (_style == "remaining") {
35✔
148
      Datetime now;
35✔
149
      if (date > now)
35✔
150
        renderStringRight(lines, width, color, Duration(date - now).formatVague(true));
34✔
151
    }
152
  }
153
}
1,749✔
154

155
////////////////////////////////////////////////////////////////////////////////
156
bool ColumnTypeDate::validate(const std::string& input) const {
9✔
157
  return input.length() ? true : false;
9✔
158
}
159

160
////////////////////////////////////////////////////////////////////////////////
161
void ColumnTypeDate::modify(Task& task, const std::string& value) {
735✔
162
  // Try to evaluate 'value'.  It might work.
163
  Variant evaluatedValue;
735✔
164
  try {
165
    Eval e;
735✔
166
    e.addSource(domSource);
735✔
167
    e.evaluateInfixExpression(value, evaluatedValue);
735✔
168
  }
735✔
169

170
  catch (...) {
124✔
171
    evaluatedValue = Variant(value);
124✔
172
  }
124✔
173

174
  // If v is duration, we need to convert it to date (and implicitly add now),
175
  // else store as date.
176
  std::string label = "   [1;37;43mMODIFICATION [0m ";
735✔
177
  if (evaluatedValue.type() == Variant::type_duration) {
735✔
178
    Context::getContext().debug(label + _name + " <-- '" +
256✔
179
                                format("{1}", format(evaluatedValue.get_duration())) + "' <-- '" +
256✔
180
                                (std::string)evaluatedValue + "' <-- '" + value + '\'');
256✔
181
    evaluatedValue.cast(Variant::type_date);
64✔
182
  } else {
183
    evaluatedValue.cast(Variant::type_date);
671✔
184
    Context::getContext().debug(label + _name + " <-- '" +
1,148✔
185
                                format("{1}", evaluatedValue.get_date()) + "' <-- '" +
1,148✔
186
                                (std::string)evaluatedValue + "' <-- '" + value + '\'');
1,148✔
187
  }
188

189
  // If a date doesn't parse (2/29/2014) then it evaluates to zero.
190
  if (value != "" && evaluatedValue.get_date() == 0)
351✔
191
    throw format("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat);
×
192

193
  time_t epoch = evaluatedValue.get_date();
351✔
194
  if (epoch < EPOCH_MIN_VALUE || epoch >= EPOCH_MAX_VALUE) {
351✔
NEW
195
    throw format("'{1}' is not a valid date.", value);
×
196
  }
197
  task.set(_name, epoch);
351✔
198
}
1,119✔
199

200
////////////////////////////////////////////////////////////////////////////////
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc