kritbit

kritbit Commit Details


Date:2015-11-21 16:05:55 (9 years 1 month ago)
Author:Natalie Adams
Branch:master
Commit:40c8ed7415f7fc377f89e85fdc327e7b71fdb03a
Parents: 17c98a46444b856f5d282c5b47042b776c30e4d7
Message:Adding cron library Adding python example runcommand

Changes:

File differences

README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Kritbit
Kritbit is a simplistic alternative to continuous integration tools like Jenkins. I've personally found Jenkins to be too cumbersome for a one man show. It's a great tool if you have the time to sit down and configure it and have it used by many people - but it was too complex for my needs.
Kritbit was created to fill a gap of needing the ability to track and run jobs.
Kritbit has 3 purposes:
1. To run a job locally and collect stats about the run
2. To run a job remotely and collect stats about the run
3. To allow an external service to phone home with stats about a job that ran
#1 is suited towards running compile and test jobs
#2 is to be crossplatform and thus will have a service that can be installed on systems. This will also have the ability to "hot run" a command remotely.
#3 is to have integration into current task scheduling systems
Kritbit is designed to be simple and flexible. It makes no assumptions about your security and only provides minimal security procedures. I am not a crypto expert - but I make tools that works. So while I cannot guarantee that big brother won't be able to decrypt messages from external services - it should be good enough for most implementations. So please, if you find that the crypto security is less than perfect I accept patches of any size, creed, or color.
scripts/rijndael.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
"""
A pure python (slow) implementation of rijndael with a decent interface
To include -
from rijndael import rijndael
To do a key setup -
r = rijndael(key, block_size = 16)
key must be a string of length 16, 24, or 32
blocksize must be 16, 24, or 32. Default is 16
To use -
ciphertext = r.encrypt(plaintext)
plaintext = r.decrypt(ciphertext)
If any strings are of the wrong length a ValueError is thrown
"""
# ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
# this code is public domain, unless someone makes
# an intellectual property claim against the reference
# code, in which case it can be made public domain by
# deleting all the comments and renaming all the variables
# Source: https://raw.githubusercontent.com/andris9/squirrelpay/master/banklink/tlslite/utils/rijndael.py
import copy
import string
#-----------------------
#TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN
#2.4.....
import os
if os.name != "java":
import exceptions
if hasattr(exceptions, "FutureWarning"):
import warnings
warnings.filterwarnings("ignore", category=FutureWarning, append=1)
#-----------------------
shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]],
[[0, 0], [1, 5], [2, 4], [3, 3]],
[[0, 0], [1, 7], [3, 5], [4, 4]]]
# [keysize][block_size]
num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
A = [[1, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 0, 0, 0, 1]]
# produce log and alog tables, needed for multiplying in the
# field GF(2^m) (generator = 3)
alog = [1]
for i in xrange(255):
j = (alog[-1] << 1) ^ alog[-1]
if j & 0x100 != 0:
j ^= 0x11B
alog.append(j)
log = [0] * 256
for i in xrange(1, 255):
log[alog[i]] = i
# multiply two elements of GF(2^m)
def mul(a, b):
if a == 0 or b == 0:
return 0
return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
# substitution box based on F^{-1}(x)
box = [[0] * 8 for i in xrange(256)]
box[1][7] = 1
for i in xrange(2, 256):
j = alog[255 - log[i]]
for t in xrange(8):
box[i][t] = (j >> (7 - t)) & 0x01
B = [0, 1, 1, 0, 0, 0, 1, 1]
# affine transform: box[i] <- B + A*box[i]
cox = [[0] * 8 for i in xrange(256)]
for i in xrange(256):
for t in xrange(8):
cox[i][t] = B[t]
for j in xrange(8):
cox[i][t] ^= A[t][j] * box[i][j]
# S-boxes and inverse S-boxes
S = [0] * 256
Si = [0] * 256
for i in xrange(256):
S[i] = cox[i][0] << 7
for t in xrange(1, 8):
S[i] ^= cox[i][t] << (7-t)
Si[S[i] & 0xFF] = i
# T-boxes
G = [[2, 1, 1, 3],
[3, 2, 1, 1],
[1, 3, 2, 1],
[1, 1, 3, 2]]
AA = [[0] * 8 for i in xrange(4)]
for i in xrange(4):
for j in xrange(4):
AA[i][j] = G[i][j]
AA[i][i+4] = 1
for i in xrange(4):
pivot = AA[i][i]
if pivot == 0:
t = i + 1
while AA[t][i] == 0 and t < 4:
t += 1
assert t != 4, 'G matrix must be invertible'
for j in xrange(8):
AA[i][j], AA[t][j] = AA[t][j], AA[i][j]
pivot = AA[i][i]
for j in xrange(8):
if AA[i][j] != 0:
AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]
for t in xrange(4):
if i != t:
for j in xrange(i+1, 8):
AA[t][j] ^= mul(AA[i][j], AA[t][i])
AA[t][i] = 0
iG = [[0] * 4 for i in xrange(4)]
for i in xrange(4):
for j in xrange(4):
iG[i][j] = AA[i][j + 4]
def mul4(a, bs):
if a == 0:
return 0
r = 0
for b in bs:
r <<= 8
if b != 0:
r = r | mul(a, b)
return r
T1 = []
T2 = []
T3 = []
T4 = []
T5 = []
T6 = []
T7 = []
T8 = []
U1 = []
U2 = []
U3 = []
U4 = []
for t in xrange(256):
s = S[t]
T1.append(mul4(s, G[0]))
T2.append(mul4(s, G[1]))
T3.append(mul4(s, G[2]))
T4.append(mul4(s, G[3]))
s = Si[t]
T5.append(mul4(s, iG[0]))
T6.append(mul4(s, iG[1]))
T7.append(mul4(s, iG[2]))
T8.append(mul4(s, iG[3]))
U1.append(mul4(t, iG[0]))
U2.append(mul4(t, iG[1]))
U3.append(mul4(t, iG[2]))
U4.append(mul4(t, iG[3]))
# round constants
rcon = [1]
r = 1
for t in xrange(1, 30):
r = mul(2, r)
rcon.append(r)
del A
del AA
del pivot
del B
del G
del box
del log
del alog
del i
del j
del r
del s
del t
del mul
del mul4
del cox
del iG
class rijndael:
def __init__(self, key, block_size = 16):
if block_size != 16 and block_size != 24 and block_size != 32:
raise ValueError('Invalid block size: ' + str(block_size))
if len(key) != 16 and len(key) != 24 and len(key) != 32:
raise ValueError('Invalid key size: ' + str(len(key)))
self.block_size = block_size
ROUNDS = num_rounds[len(key)][block_size]
BC = block_size / 4
# encryption round keys
Ke = [[0] * BC for i in xrange(ROUNDS + 1)]
# decryption round keys
Kd = [[0] * BC for i in xrange(ROUNDS + 1)]
ROUND_KEY_COUNT = (ROUNDS + 1) * BC
KC = len(key) / 4
# copy user material bytes into temporary ints
tk = []
for i in xrange(0, KC):
tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) |
(ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3]))
# copy values into round key arrays
t = 0
j = 0
while j < KC and t < ROUND_KEY_COUNT:
Ke[t / BC][t % BC] = tk[j]
Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
j += 1
t += 1
tt = 0
rconpointer = 0
while t < ROUND_KEY_COUNT:
# extrapolate using phi (the round key evolution function)
tt = tk[KC - 1]
tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \
(S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \
(S[ tt & 0xFF] & 0xFF) << 8 ^ \
(S[(tt >> 24) & 0xFF] & 0xFF) ^ \
(rcon[rconpointer] & 0xFF) << 24
rconpointer += 1
if KC != 8:
for i in xrange(1, KC):
tk[i] ^= tk[i-1]
else:
for i in xrange(1, KC / 2):
tk[i] ^= tk[i-1]
tt = tk[KC / 2 - 1]
tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \
(S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \
(S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
(S[(tt >> 24) & 0xFF] & 0xFF) << 24
for i in xrange(KC / 2 + 1, KC):
tk[i] ^= tk[i-1]
# copy values into round key arrays
j = 0
while j < KC and t < ROUND_KEY_COUNT:
Ke[t / BC][t % BC] = tk[j]
Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
j += 1
t += 1
# inverse MixColumn where needed
for r in xrange(1, ROUNDS):
for j in xrange(BC):
tt = Kd[r][j]
Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
U2[(tt >> 16) & 0xFF] ^ \
U3[(tt >> 8) & 0xFF] ^ \
U4[ tt & 0xFF]
self.Ke = Ke
self.Kd = Kd
def encrypt(self, plaintext):
if len(plaintext) != self.block_size:
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
Ke = self.Ke
BC = self.block_size / 4
ROUNDS = len(Ke) - 1
if BC == 4:
SC = 0
elif BC == 6:
SC = 1
else:
SC = 2
s1 = shifts[SC][1][0]
s2 = shifts[SC][2][0]
s3 = shifts[SC][3][0]
a = [0] * BC
# temporary work array
t = []
# plaintext to ints + key
for i in xrange(BC):
t.append((ord(plaintext[i * 4 ]) << 24 |
ord(plaintext[i * 4 + 1]) << 16 |
ord(plaintext[i * 4 + 2]) << 8 |
ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i])
# apply round transforms
for r in xrange(1, ROUNDS):
for i in xrange(BC):
a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^
T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^
T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i]
t = copy.copy(a)
# last round is special
result = []
for i in xrange(BC):
tt = Ke[ROUNDS][i]
result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
return string.join(map(chr, result), '')
def decrypt(self, ciphertext):
if len(ciphertext) != self.block_size:
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
Kd = self.Kd
BC = self.block_size / 4
ROUNDS = len(Kd) - 1
if BC == 4:
SC = 0
elif BC == 6:
SC = 1
else:
SC = 2
s1 = shifts[SC][1][1]
s2 = shifts[SC][2][1]
s3 = shifts[SC][3][1]
a = [0] * BC
# temporary work array
t = [0] * BC
# ciphertext to ints + key
for i in xrange(BC):
t[i] = (ord(ciphertext[i * 4 ]) << 24 |
ord(ciphertext[i * 4 + 1]) << 16 |
ord(ciphertext[i * 4 + 2]) << 8 |
ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i]
# apply round transforms
for r in xrange(1, ROUNDS):
for i in xrange(BC):
a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^
T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i]
t = copy.copy(a)
# last round is special
result = []
for i in xrange(BC):
tt = Kd[ROUNDS][i]
result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
return string.join(map(chr, result), '')
def encrypt(key, block):
return rijndael(key, len(block)).encrypt(block)
def decrypt(key, block):
return rijndael(key, len(block)).decrypt(block)
def test():
def t(kl, bl):
b = 'b' * bl
r = rijndael('a' * kl, bl)
assert r.decrypt(r.encrypt(b)) == b
t(16, 16)
t(16, 24)
t(16, 32)
t(24, 16)
t(24, 24)
t(24, 32)
t(32, 16)
t(32, 24)
t(32, 32)
scripts/runcommand.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/bin/python
import shlex
from subprocess import Popen, PIPE
import sys
import rijndael
import base64
import string
import random
import json
import time
import hashlib
import pycurl
from urllib import urlencode
try:
from cStringIO import StringIO
except ImportError:
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
SHARED_KEY = ""
HASH = ""
# source http://stackoverflow.com/a/8232171/195722
KEY_SIZE = 16
BLOCK_SIZE = 32
# JamPAo52/smpiObKa8p/MY5WAeDww0cOg9KiG6gMAYQ=
def curl_post(url, postvals, header = []):
buffer = StringIO()
cobj = pycurl.Curl()
cobj.setopt(pycurl.URL, url)
cobj.setopt(pycurl.POST, 1)
cobj.setopt(pycurl.WRITEDATA, buffer)
postdata = urlencode(postvals)
cobj.setopt(pycurl.POSTFIELDS, postdata)
cobj.setopt(pycurl.HTTPHEADER, header)
cobj.perform()
cobj.close()
return buffer
def get_exitcode_stdout_stderr(cmd):
"""
Execute the external command and get its exitcode, stdout and stderr.
"""
args = shlex.split(cmd)
proc = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
out, err = proc.communicate()
exitcode = proc.returncode
#
return exitcode, out, err
def encrypt(key, plaintext):
padded_key = key.ljust(KEY_SIZE, '\0')
padded_text = plaintext + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE) * '\0'
# could also be one of
#if len(plaintext) % BLOCK_SIZE != 0:
# padded_text = plaintext.ljust((len(plaintext) / BLOCK_SIZE) + 1 * BLOCKSIZE), '\0')
# -OR-
#padded_text = plaintext.ljust((len(plaintext) + (BLOCK_SIZE - len(plaintext) % BLOCK_SIZE)), '\0')
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
ciphertext = ''
for start in range(0, len(padded_text), BLOCK_SIZE):
ciphertext += r.encrypt(padded_text[start:start+BLOCK_SIZE])
encoded = base64.b64encode(ciphertext)
return encoded
def decrypt(key, encoded):
padded_key = key.ljust(KEY_SIZE, '\0')
ciphertext = base64.b64decode(encoded)
r = rijndael.rijndael(padded_key, BLOCK_SIZE)
padded_text = ''
for start in range(0, len(ciphertext), BLOCK_SIZE):
padded_text += r.decrypt(ciphertext[start:start+BLOCK_SIZE])
plaintext = padded_text.split('\x00', 1)[0]
return plaintext
start_time = time.time()
exitcode, out, err = get_exitcode_stdout_stderr(sys.argv[1])
total = time.time() - start_time
nonce = ''.join(random.SystemRandom().choice(string.hexdigits + string.digits) for _ in range(10))
message = {}
message["nonce"] = nonce
message["message"] = json.dumps({"output":out, "time_taken": total, "result": exitcode})
message["signature"] = hashlib.sha256(message["message"] + nonce + HASH).hexdigest()
print encrypt(SHARED_KEY, "test")
message["message"] = encrypt(SHARED_KEY, message["message"])
print json.dumps(message)
print curl_post("http://192.168.128.36:8080/service/upload/5/", {"data": json.dumps(message)}).getvalue()
web/application/controllers/base.php
99
1010
1111
12
1213
1314
1415
......
108109
109110
110111
111
112
112
113
114
115
113116
114117
115118
protected $session = null;
protected $sessionData = null;
protected $loginRequired = true;
protected $sessionRequired = true;
protected function isLoggedIn() {
if (!$this->sessionData && !isset($this->sessionData->userId)) {
header("Location: /login");
\vendor\DB\DB::$c = $this->pdo;
}
$this->setupSession();
$this->setupUser();
if ($this->sessionRequired) {
$this->setupSession();
$this->setupUser();
}
if ($this->loginRequired && !$this->user) {
$this->login();
}
web/application/controllers/history.php
1818
1919
2020
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2137
2238
2339
return true;
}
public function runscript($jobId) {
$jobObject = \application\models\Jobs::getByField("id", $jobId);
if ($this->checkAccess($jobObject[0])) {
header("Content-Type: text/plain");
echo $jobObject[0]->runScript;
}
}
public function failscript($jobId) {
$jobObject = \application\models\Jobs::getByField("id", $jobId);
if ($this->checkAccess($jobObject[0])) {
header("Content-Type: text/plain");
echo $jobObject[0]->failScript;
}
}
public function view($id) {
$idArr = explode("-", $id);
try {
web/application/controllers/job.php
11
22
3
4
35
46
57
......
5557
5658
5759
60
61
62
63
64
65
66
67
68
69
70
71
72
5873
<?php
use \vendor\DB\DB;
class job extends base {
public function add() {
if (!isset($_POST["jobName"])) {
}
}
public function search() {
if (isset($_GET["q"])) {
$histories = DB::fetch("
SELECT
jobs.id as job_id, jobs.jobName, histories.id, run_date, time_taken, result
FROM histories
INNER JOIN jobs ON jobs.user_id = ? AND histories.jobs_id = jobs.id
WHERE output LIKE ?
", [$this->user->id, "%" . $_GET["q"] . "%"]);
echo $this->loadRender("search.html", ["search" => $_GET["q"], "histories" => $histories]);
}
}
}
web/application/controllers/service.php
33
44
55
6
7
8
9
610
711
812
913
1014
11
15
1216
1317
1418
1519
16
20
1721
1822
1923
2024
2125
22
23
24
25
26
27
26
27
28
29
30
31
32
33
34
35
36
2837
2938
3039
......
4150
4251
4352
44
45
46
47
48
49
50
51
53
54
55
56
57
58
59
60
61
5262
53
54
55
56
57
58
59
60
61
62
63
64
65
66
63
64
6765
68
69
66
67
7068
71
69
7270
7371
7472
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
7598
76
77
78
79
80
81
82
83
84
85
86
87
8899
89100
101
102
103
90104
91
92105
106
93107
94108
95109
use \vendor\DB\DB;
class service extends base {
protected $loginRequired = false;
protected $sessionRequired = false;
/**
* This service will expect a JSON POST data of:
* ["data"] => {"nonce": "randomString", "message": "cipherText", "signature": "abcdef"}
* Signature will be a sha256 of the message pre-encrypt with nonce appended to the end
* ie
* {JSON} + "|" nonce + "|" + sharedhash
* {JSON} + nonce + sharedhash
* Note: sharedhash should NOT be the sharedkey that is used to encrypt the message
*
*
* Unencrypted cipherText will look like
* {"output": "stdout of run", "run_date": "2015-01-01", "time_taken": 10, "result": 0}
* {"output": "stdout of run", "time_taken": 10, "result": 0}
* Just like in most modern programs - a result of anything but 0 indicates an error
*
* @param $jobId
*/
public function upload($jobId) {
if ($jobId && is_int($jobId)) {
/** @var \application\models\Jobs $job */
$job = \application\models\Jobs::getByField("id", $jobId)[0];
//decrypt message
$data = json_decode($_POST["data"]);
$rawMessage = aes_decrypt($job->sharedkey,$data["message"]);
if ($jobId && is_numeric($jobId)) {
try {
/** @var \application\models\Jobs $job */
$job = \application\models\Jobs::getByField("id", $jobId)[0];
//decrypt message
$data = json_decode($_POST["data"], true);
$rawMessage = aes_decrypt($job->sharedkey, $data["message"]);
} catch (\Exception $e) {
echo $e;
exit(1);
}
// if decryption was successful -
// check signature
if (in_array($_SERVER["REMOTE_ADDR"], $this->config["ACCEPTED_IPS"])) { // not very secure - but worst case they fire off the run early
if (!file_exists("/tmp/kritbot")) {
touch("/tmp/kritbot");
/** @var \application\models\Jobs[] $jobs */
$jobs = DB::fetchObject("SELECT * FROM jobs", "\\application\\models\\Jobs");
foreach($jobs as $job) {
if ($job->runType == 1) {
$cron = Cron\CronExpression::factory($job->cron);
if ($cron->isDue() || $job->force_run == 1) {
$output = [];
$returnVar = 0;
try {
/** @var \application\models\Jobs[] $jobs */
$jobs = DB::fetchObject("SELECT * FROM jobs", "\\application\\models\\Jobs");
foreach ($jobs as $job) {
if ($job->runType == 1) {
$cron = Cron\CronExpression::factory($job->cron);
if ($cron->isDue() || $job->force_run == 1) {
$output = [];
$returnVar = 0;
$start = microtime(true);
// grumble grumble something something windows
if (stripos(php_uname("s"), "Win") !== false) {
file_put_contents("/tmp/kritscript.bat", $job->runScript);
exec("c:\\windows\\system32\\cmd.exe /c c:/tmp/kritscript.bat", $output, $returnVar);
} else {
file_put_contents("/tmp/kritscript", $job->runScript);
exec("/tmp/kritscript", $output, $returnVar);
chmod("/tmp/kritscript", 0777);
}
$end = microtime(true);
$delta = $end - $start;
$scriptOutput = implode("\n", $output);
if ($returnVar != 0) {
$start = microtime(true);
// grumble grumble something something windows
if (stripos(php_uname("s"), "Win") !== false) {
file_put_contents("/tmp/kritscript.bat", $job->failScript);
exec("c:\\windows\\system32\\cmd.exe /c c:/tmp/kirtscript.bat");
file_put_contents("/tmp/kritscript.bat", $job->runScript);
exec("c:\\windows\\system32\\cmd.exe /c c:/tmp/kritscript.bat", $output, $returnVar);
} else {
file_put_contents("/tmp/kritscript", $job->failScript);
file_put_contents("/tmp/kritscript", $job->runScript);
exec("/tmp/kritscript", $output, $returnVar);
chmod("/tmp/kritscript", 0777);
}
$end = microtime(true);
$delta = $end - $start;
$scriptOutput = implode("\n", $output);
if ($returnVar != 0) {
if (stripos(php_uname("s"), "Win") !== false) {
file_put_contents("/tmp/kritscript.bat", $job->failScript);
exec("c:\\windows\\system32\\cmd.exe /c c:/tmp/kirtscript.bat");
} else {
file_put_contents("/tmp/kritscript", $job->failScript);
exec("/tmp/kritscript", $output, $returnVar);
chmod("/tmp/kritscript", 0777);
}
}
$historyObj = new \application\models\Histories();
$historyObj->output = $scriptOutput;
$historyObj->result = $returnVar;
$historyObj->time_taken = $delta;
$historyObj->jobs_id = $job->id;
$now = date("Y-m-d H:i:s");
$historyObj->run_date = $now;
$historyObj->save();
$job->force_run = 0;
$job->last_run = $now;
$job->last_result = $returnVar;
$job->save();
}
$historyObj = new \application\models\Histories();
$historyObj->output = $scriptOutput;
$historyObj->result = $returnVar;
$historyObj->time_taken = $delta;
$historyObj->jobs_id = $job->id;
$now = date("Y-m-d H:i:s");
$historyObj->run_date = $now;
$historyObj->save();
$job->force_run = 0;
$job->last_run = $now;
$job->last_result = $returnVar;
$job->save();
}
}
unlink("/tmp/kritbot");
} catch (\Exception $e) {
unlink("/tmp/kritbot");
}
unlink("/tmp/kritbot");
}
}
}
}
web/application/views/add.html
8585
8686
8787
88
88
89
8990
9091
9192
......
110111
111112
112113
114
115
116
117
118
119
120
113121
114122
115123
<label class="col-sm-2 control-label" for="private">Require login to view history:</label>
<div class="checkbox">
<label>
<input {% if job.view_private == 1 %}checked="checked"{% endif %} name="view_private" id="private" type="checkbox"> Yes
<input {% if job.view_private == 1 %}checked="checked"{% endif %} id="private" type="checkbox"> Yes
<input type="hidden" id="view_private" name="view_private" value="{{job.view_private}}">
</label>
</div>
</div>
$("#selectedType").text($(this).text());
//$('#datebox').val($(this).text());
});
$("#private").on("change", function () {
if ($(this).is(":checked")) {
$("#view_private").val(1);
} else {
$("#view_private").val(0);
}
});
});
</script>
web/application/views/base.html
1818
1919
2020
21
21
22
23
2224
2325
2426
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js" integrity="sha512-K1qjQ+NcF2TYO/eI3M6v8EiNYZfA95pQumfvcVrTHtwQVDG+aHRqLi/ETn2uB+1JqwYqVG3LIvdm9lj6imS/pQ==" crossorigin="anonymous"></script>
<title>{{title}}</title>
<style type="text/css">
.text-center {
text-align: center;
}
</style>
</head>
<body>
web/application/views/history.html
1010
1111
1212
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1331
1432
1533
<div class="row">
<div class="col-md-4 col-md-offset-5">{{job.comments|nl2br|safe}}</div>
</div>
<hr>
<table align="center" style="width: 30%;" class="table table-hover">
<thead>
<tr>
<th class="text-center">Run Script</th>
<th class="text-center">Fail Script</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center"><a class="btn btn-default" href="/history/runscript/{{job.id}}/" role="button">View Script</a></td>
<td class="text-center"><a class="btn btn-default" href="/history/failscript/{{job.id}}/" role="button">View Script</a></td>
</tr>
</tbody>
</table>
<hr>
<table align="center" style="width: 60%;" class="table table-hover">
web/application/views/menu.html
2020
2121
2222
23
23
2424
25
25
2626
2727
2828
</li>
</ul>
<form class="navbar-form navbar-left" role="search">
<form class="navbar-form navbar-left" method="get" role="search" action="/job/search/">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
<input type="text" class="form-control" name="q" value="{{search}}" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
web/application/views/search.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1>Search Results</h1>
</div>
<table align="center" style="width: 60%;" class="table table-hover">
<thead>
<tr>
<th>Job Name</th>
<th>Output</th>
<th>Run Date</th>
<th>Time Taken</th>
<th>Result</th>
</tr>
</thead>
<tbody>
{% if !histories %}
<tr>
<td align="center" colspan="4">No results</td>
</tr>
{% endif %}
{% for history in histories %}
<tr>
<td><a href="/history/view/{{history.jobName}}-{{history.job_id}}">{{history.jobName}}</a></td>
<td><a class="btn btn-default" href="/history/log/{{history.job_id}}/{{history.id}}/" role="button">View</a></td>
<td>{{history.run_date}}</td>
<td>{{history.time_taken}}</td>
<td>{{history.result}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
web/system/vendor/Cron/AbstractField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<?php
namespace Cron;
/**
* Abstract CRON expression field
*/
abstract class AbstractField implements FieldInterface
{
/**
* Check to see if a field is satisfied by a value
*
* @param string $dateValue Date value to check
* @param string $value Value to test
*
* @return bool
*/
public function isSatisfied($dateValue, $value)
{
if ($this->isIncrementsOfRanges($value)) {
return $this->isInIncrementsOfRanges($dateValue, $value);
} elseif ($this->isRange($value)) {
return $this->isInRange($dateValue, $value);
}
return $value == '*' || $dateValue == $value;
}
/**
* Check if a value is a range
*
* @param string $value Value to test
*
* @return bool
*/
public function isRange($value)
{
return strpos($value, '-') !== false;
}
/**
* Check if a value is an increments of ranges
*
* @param string $value Value to test
*
* @return bool
*/
public function isIncrementsOfRanges($value)
{
return strpos($value, '/') !== false;
}
/**
* Test if a value is within a range
*
* @param string $dateValue Set date value
* @param string $value Value to test
*
* @return bool
*/
public function isInRange($dateValue, $value)
{
$parts = array_map('trim', explode('-', $value, 2));
return $dateValue >= $parts[0] && $dateValue <= $parts[1];
}
/**
* Test if a value is within an increments of ranges (offset[-to]/step size)
*
* @param string $dateValue Set date value
* @param string $value Value to test
*
* @return bool
*/
public function isInIncrementsOfRanges($dateValue, $value)
{
$parts = array_map('trim', explode('/', $value, 2));
$stepSize = isset($parts[1]) ? $parts[1] : 0;
if (($parts[0] == '*' || $parts[0] === '0') && 0 !== $stepSize) {
return (int) $dateValue % $stepSize == 0;
}
$range = explode('-', $parts[0], 2);
$offset = $range[0];
$to = isset($range[1]) ? $range[1] : $dateValue;
// Ensure that the date value is within the range
if ($dateValue < $offset || $dateValue > $to) {
return false;
}
if ($dateValue > $offset && 0 === $stepSize) {
return false;
}
for ($i = $offset; $i <= $to; $i+= $stepSize) {
if ($i == $dateValue) {
return true;
}
}
return false;
}
}
web/system/vendor/Cron/CronExpression.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
<?php
namespace Cron;
/**
* CRON expression parser that can determine whether or not a CRON expression is
* due to run, the next run date and previous run date of a CRON expression.
* The determinations made by this class are accurate if checked run once per
* minute (seconds are dropped from date time comparisons).
*
* Schedule parts must map to:
* minute [0-59], hour [0-23], day of month, month [1-12|JAN-DEC], day of week
* [1-7|MON-SUN], and an optional year.
*
* @link http://en.wikipedia.org/wiki/Cron
*/
class CronExpression
{
const MINUTE = 0;
const HOUR = 1;
const DAY = 2;
const MONTH = 3;
const WEEKDAY = 4;
const YEAR = 5;
/**
* @var array CRON expression parts
*/
private $cronParts;
/**
* @var FieldFactory CRON field factory
*/
private $fieldFactory;
/**
* @var array Order in which to test of cron parts
*/
private static $order = array(self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE);
/**
* Factory method to create a new CronExpression.
*
* @param string $expression The CRON expression to create. There are
* several special predefined values which can be used to substitute the
* CRON expression:
*
* `@yearly`, `@annually` - Run once a year, midnight, Jan. 1 - 0 0 1 1 *
* `@monthly` - Run once a month, midnight, first of month - 0 0 1 * *
* `@weekly` - Run once a week, midnight on Sun - 0 0 * * 0
* `@daily` - Run once a day, midnight - 0 0 * * *
* `@hourly` - Run once an hour, first minute - 0 * * * *
* @param FieldFactory $fieldFactory Field factory to use
*
* @return CronExpression
*/
public static function factory($expression, FieldFactory $fieldFactory = null)
{
$mappings = array(
'@yearly' => '0 0 1 1 *',
'@annually' => '0 0 1 1 *',
'@monthly' => '0 0 1 * *',
'@weekly' => '0 0 * * 0',
'@daily' => '0 0 * * *',
'@hourly' => '0 * * * *'
);
if (isset($mappings[$expression])) {
$expression = $mappings[$expression];
}
return new static($expression, $fieldFactory ?: new FieldFactory());
}
/**
* Validate a CronExpression.
*
* @param string $expression The CRON expression to validate.
*
* @return bool True if a valid CRON expression was passed. False if not.
* @see Cron\CronExpression::factory
*/
public static function isValidExpression($expression)
{
try {
self::factory($expression);
} catch (\InvalidArgumentException $e) {
return false;
}
return true;
}
/**
* Parse a CRON expression
*
* @param string $expression CRON expression (e.g. '8 * * * *')
* @param FieldFactory $fieldFactory Factory to create cron fields
*/
public function __construct($expression, FieldFactory $fieldFactory)
{
$this->fieldFactory = $fieldFactory;
$this->setExpression($expression);
}
/**
* Set or change the CRON expression
*
* @param string $value CRON expression (e.g. 8 * * * *)
*
* @return CronExpression
* @throws \InvalidArgumentException if not a valid CRON expression
*/
public function setExpression($value)
{
$this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
if (count($this->cronParts) < 5) {
throw new \InvalidArgumentException(
$value . ' is not a valid CRON expression'
);
}
foreach ($this->cronParts as $position => $part) {
$this->setPart($position, $part);
}
return $this;
}
/**
* Set part of the CRON expression
*
* @param int $position The position of the CRON expression to set
* @param string $value The value to set
*
* @return CronExpression
* @throws \InvalidArgumentException if the value is not valid for the part
*/
public function setPart($position, $value)
{
if (!$this->fieldFactory->getField($position)->validate($value)) {
throw new \InvalidArgumentException(
'Invalid CRON field value ' . $value . ' at position ' . $position
);
}
$this->cronParts[$position] = $value;
return $this;
}
/**
* Get a next run date relative to the current date or a specific date
*
* @param string|\DateTime $currentTime Relative calculation date
* @param int $nth Number of matches to skip before returning a
* matching next run date. 0, the default, will return the current
* date and time if the next run date falls on the current date and
* time. Setting this value to 1 will skip the first match and go to
* the second match. Setting this value to 2 will skip the first 2
* matches and so on.
* @param bool $allowCurrentDate Set to TRUE to return the current date if
* it matches the cron expression.
*
* @return \DateTime
* @throws \RuntimeException on too many iterations
*/
public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
{
return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate);
}
/**
* Get a previous run date relative to the current date or a specific date
*
* @param string|\DateTime $currentTime Relative calculation date
* @param int $nth Number of matches to skip before returning
* @param bool $allowCurrentDate Set to TRUE to return the
* current date if it matches the cron expression
*
* @return \DateTime
* @throws \RuntimeException on too many iterations
* @see Cron\CronExpression::getNextRunDate
*/
public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
{
return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate);
}
/**
* Get multiple run dates starting at the current date or a specific date
*
* @param int $total Set the total number of dates to calculate
* @param string|\DateTime $currentTime Relative calculation date
* @param bool $invert Set to TRUE to retrieve previous dates
* @param bool $allowCurrentDate Set to TRUE to return the
* current date if it matches the cron expression
*
* @return array Returns an array of run dates
*/
public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false)
{
$matches = array();
for ($i = 0; $i < max(0, $total); $i++) {
try {
$matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
} catch (\RuntimeException $e) {
break;
}
}
return $matches;
}
/**
* Get all or part of the CRON expression
*
* @param string $part Specify the part to retrieve or NULL to get the full
* cron schedule string.
*
* @return string|null Returns the CRON expression, a part of the
* CRON expression, or NULL if the part was specified but not found
*/
public function getExpression($part = null)
{
if (null === $part) {
return implode(' ', $this->cronParts);
} elseif (array_key_exists($part, $this->cronParts)) {
return $this->cronParts[$part];
}
return null;
}
/**
* Helper method to output the full expression.
*
* @return string Full CRON expression
*/
public function __toString()
{
return $this->getExpression();
}
/**
* Determine if the cron is due to run based on the current date or a
* specific date. This method assumes that the current number of
* seconds are irrelevant, and should be called once per minute.
*
* @param string|\DateTime $currentTime Relative calculation date
*
* @return bool Returns TRUE if the cron is due to run or FALSE if not
*/
public function isDue($currentTime = 'now')
{
if ('now' === $currentTime) {
$currentDate = date('Y-m-d H:i');
$currentTime = strtotime($currentDate);
} elseif ($currentTime instanceof \DateTime) {
$currentDate = clone $currentTime;
// Ensure time in 'current' timezone is used
$currentDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
$currentDate = $currentDate->format('Y-m-d H:i');
$currentTime = strtotime($currentDate);
} else {
$currentTime = new \DateTime($currentTime);
$currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0);
$currentDate = $currentTime->format('Y-m-d H:i');
$currentTime = $currentTime->getTimeStamp();
}
try {
return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime;
} catch (\Exception $e) {
return false;
}
}
/**
* Get the next or previous run date of the expression relative to a date
*
* @param string|\DateTime $currentTime Relative calculation date
* @param int $nth Number of matches to skip before returning
* @param bool $invert Set to TRUE to go backwards in time
* @param bool $allowCurrentDate Set to TRUE to return the
* current date if it matches the cron expression
*
* @return \DateTime
* @throws \RuntimeException on too many iterations
*/
protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false)
{
if ($currentTime instanceof \DateTime) {
$currentDate = clone $currentTime;
} else {
$currentDate = new \DateTime($currentTime ?: 'now');
$currentDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
}
$currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0);
$nextRun = clone $currentDate;
$nth = (int) $nth;
// We don't have to satisfy * or null fields
$parts = array();
$fields = array();
foreach (self::$order as $position) {
$part = $this->getExpression($position);
if (null === $part || '*' === $part) {
continue;
}
$parts[$position] = $part;
$fields[$position] = $this->fieldFactory->getField($position);
}
// Set a hard limit to bail on an impossible date
for ($i = 0; $i < 1000; $i++) {
foreach ($parts as $position => $part) {
$satisfied = false;
// Get the field object used to validate this part
$field = $fields[$position];
// Check if this is singular or a list
if (strpos($part, ',') === false) {
$satisfied = $field->isSatisfiedBy($nextRun, $part);
} else {
foreach (array_map('trim', explode(',', $part)) as $listPart) {
if ($field->isSatisfiedBy($nextRun, $listPart)) {
$satisfied = true;
break;
}
}
}
// If the field is not satisfied, then start over
if (!$satisfied) {
$field->increment($nextRun, $invert);
continue 2;
}
}
// Skip this match if needed
if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
$this->fieldFactory->getField(0)->increment($nextRun, $invert);
continue;
}
return $nextRun;
}
// @codeCoverageIgnoreStart
throw new \RuntimeException('Impossible CRON expression');
// @codeCoverageIgnoreEnd
}
}
web/system/vendor/Cron/DayOfMonthField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
namespace Cron;
/**
* Day of month field. Allows: * , / - ? L W
*
* 'L' stands for "last" and specifies the last day of the month.
*
* The 'W' character is used to specify the weekday (Monday-Friday) nearest the
* given day. As an example, if you were to specify "15W" as the value for the
* day-of-month field, the meaning is: "the nearest weekday to the 15th of the
* month". So if the 15th is a Saturday, the trigger will fire on Friday the
* 14th. If the 15th is a Sunday, the trigger will fire on Monday the 16th. If
* the 15th is a Tuesday, then it will fire on Tuesday the 15th. However if you
* specify "1W" as the value for day-of-month, and the 1st is a Saturday, the
* trigger will fire on Monday the 3rd, as it will not 'jump' over the boundary
* of a month's days. The 'W' character can only be specified when the
* day-of-month is a single day, not a range or list of days.
*
* @author Michael Dowling <mtdowling@gmail.com>
*/
class DayOfMonthField extends AbstractField
{
/**
* Get the nearest day of the week for a given day in a month
*
* @param int $currentYear Current year
* @param int $currentMonth Current month
* @param int $targetDay Target day of the month
*
* @return \DateTime Returns the nearest date
*/
private static function getNearestWeekday($currentYear, $currentMonth, $targetDay)
{
$tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT);
$target = \DateTime::createFromFormat('Y-m-d', "$currentYear-$currentMonth-$tday");
$currentWeekday = (int) $target->format('N');
if ($currentWeekday < 6) {
return $target;
}
$lastDayOfMonth = $target->format('t');
foreach (array(-1, 1, -2, 2) as $i) {
$adjusted = $targetDay + $i;
if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) {
$target->setDate($currentYear, $currentMonth, $adjusted);
if ($target->format('N') < 6 && $target->format('m') == $currentMonth) {
return $target;
}
}
}
}
public function isSatisfiedBy(\DateTime $date, $value)
{
// ? states that the field value is to be skipped
if ($value == '?') {
return true;
}
$fieldValue = $date->format('d');
// Check to see if this is the last day of the month
if ($value == 'L') {
return $fieldValue == $date->format('t');
}
// Check to see if this is the nearest weekday to a particular value
if (strpos($value, 'W')) {
// Parse the target day
$targetDay = substr($value, 0, strpos($value, 'W'));
// Find out if the current day is the nearest day of the week
return $date->format('j') == self::getNearestWeekday(
$date->format('Y'),
$date->format('m'),
$targetDay
)->format('j');
}
return $this->isSatisfied($date->format('d'), $value);
}
public function increment(\DateTime $date, $invert = false)
{
if ($invert) {
$date->modify('previous day');
$date->setTime(23, 59);
} else {
$date->modify('next day');
$date->setTime(0, 0);
}
return $this;
}
public function validate($value)
{
return (bool) preg_match('/^[\*,\/\-\?LW0-9A-Za-z]+$/', $value);
}
}
web/system/vendor/Cron/DayOfWeekField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<?php
namespace Cron;
/**
* Day of week field. Allows: * / , - ? L #
*
* Days of the week can be represented as a number 0-7 (0|7 = Sunday)
* or as a three letter string: SUN, MON, TUE, WED, THU, FRI, SAT.
*
* 'L' stands for "last". It allows you to specify constructs such as
* "the last Friday" of a given month.
*
* '#' is allowed for the day-of-week field, and must be followed by a
* number between one and five. It allows you to specify constructs such as
* "the second Friday" of a given month.
*/
class DayOfWeekField extends AbstractField
{
public function isSatisfiedBy(\DateTime $date, $value)
{
if ($value == '?') {
return true;
}
// Convert text day of the week values to integers
$value = $this->convertLiterals($value);
$currentYear = $date->format('Y');
$currentMonth = $date->format('m');
$lastDayOfMonth = $date->format('t');
// Find out if this is the last specific weekday of the month
if (strpos($value, 'L')) {
$weekday = str_replace('7', '0', substr($value, 0, strpos($value, 'L')));
$tdate = clone $date;
$tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
while ($tdate->format('w') != $weekday) {
$tdate->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
}
return $date->format('j') == $lastDayOfMonth;
}
// Handle # hash tokens
if (strpos($value, '#')) {
list($weekday, $nth) = explode('#', $value);
// 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601
if ($weekday === '0') {
$weekday = 7;
}
// Validate the hash fields
if ($weekday < 0 || $weekday > 7) {
throw new \InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given");
}
if ($nth > 5) {
throw new \InvalidArgumentException('There are never more than 5 of a given weekday in a month');
}
// The current weekday must match the targeted weekday to proceed
if ($date->format('N') != $weekday) {
return false;
}
$tdate = clone $date;
$tdate->setDate($currentYear, $currentMonth, 1);
$dayCount = 0;
$currentDay = 1;
while ($currentDay < $lastDayOfMonth + 1) {
if ($tdate->format('N') == $weekday) {
if (++$dayCount >= $nth) {
break;
}
}
$tdate->setDate($currentYear, $currentMonth, ++$currentDay);
}
return $date->format('j') == $currentDay;
}
// Handle day of the week values
if (strpos($value, '-')) {
$parts = explode('-', $value);
if ($parts[0] == '7') {
$parts[0] = '0';
} elseif ($parts[1] == '0') {
$parts[1] = '7';
}
$value = implode('-', $parts);
}
// Test to see which Sunday to use -- 0 == 7 == Sunday
$format = in_array(7, str_split($value)) ? 'N' : 'w';
$fieldValue = $date->format($format);
return $this->isSatisfied($fieldValue, $value);
}
public function increment(\DateTime $date, $invert = false)
{
if ($invert) {
$date->modify('-1 day');
$date->setTime(23, 59, 0);
} else {
$date->modify('+1 day');
$date->setTime(0, 0, 0);
}
return $this;
}
public function validate($value)
{
$value = $this->convertLiterals($value);
foreach (explode(',', $value) as $expr) {
if (!preg_match('/^(\*|[0-7](L?|#[1-5]))([\/\,\-][0-7]+)*$/', $expr)) {
return false;
}
}
return true;
}
private function convertLiterals($string)
{
return str_ireplace(
array('SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'),
range(0, 6),
$string
);
}
}
web/system/vendor/Cron/FieldFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
namespace Cron;
/**
* CRON field factory implementing a flyweight factory
* @link http://en.wikipedia.org/wiki/Cron
*/
class FieldFactory
{
/**
* @var array Cache of instantiated fields
*/
private $fields = array();
/**
* Get an instance of a field object for a cron expression position
*
* @param int $position CRON expression position value to retrieve
*
* @return FieldInterface
* @throws InvalidArgumentException if a position is not valid
*/
public function getField($position)
{
if (!isset($this->fields[$position])) {
switch ($position) {
case 0:
$this->fields[$position] = new MinutesField();
break;
case 1:
$this->fields[$position] = new HoursField();
break;
case 2:
$this->fields[$position] = new DayOfMonthField();
break;
case 3:
$this->fields[$position] = new MonthField();
break;
case 4:
$this->fields[$position] = new DayOfWeekField();
break;
case 5:
$this->fields[$position] = new YearField();
break;
default:
throw new \InvalidArgumentException(
$position . ' is not a valid position'
);
}
}
return $this->fields[$position];
}
}
web/system/vendor/Cron/FieldInterface.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
namespace Cron;
/**
* CRON field interface
*/
interface FieldInterface
{
/**
* Check if the respective value of a DateTime field satisfies a CRON exp
*
* @param DateTime $date DateTime object to check
* @param string $value CRON expression to test against
*
* @return bool Returns TRUE if satisfied, FALSE otherwise
*/
public function isSatisfiedBy(\DateTime $date, $value);
/**
* When a CRON expression is not satisfied, this method is used to increment
* or decrement a DateTime object by the unit of the cron field
*
* @param DateTime $date DateTime object to change
* @param bool $invert (optional) Set to TRUE to decrement
*
* @return FieldInterface
*/
public function increment(\DateTime $date, $invert = false);
/**
* Validates a CRON expression for a given field
*
* @param string $value CRON expression value to validate
*
* @return bool Returns TRUE if valid, FALSE otherwise
*/
public function validate($value);
}
web/system/vendor/Cron/HoursField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
namespace Cron;
/**
* Hours field. Allows: * , / -
*/
class HoursField extends AbstractField
{
public function isSatisfiedBy(\DateTime $date, $value)
{
return $this->isSatisfied($date->format('H'), $value);
}
public function increment(\DateTime $date, $invert = false)
{
// Change timezone to UTC temporarily. This will
// allow us to go back or forwards and hour even
// if DST will be changed between the hours.
$timezone = $date->getTimezone();
$localMinutes = $date->format('i');
$date->setTimezone(new \DateTimeZone('UTC'));
// handle timezones with non-hour-offsets
$utcMinutes = $date->format('i');
$minDiff = $localMinutes - $utcMinutes;
if ($invert) {
$date->modify('-1 hour');
$date->setTime($date->format('H'), 59 - $minDiff);
} else {
$date->modify('+1 hour');
$date->setTime($date->format('H'), 0 - $minDiff);
}
$date->setTimezone($timezone);
return $this;
}
public function validate($value)
{
return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
}
}
web/system/vendor/Cron/LICENSE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Copyright (c) 2011 Michael Dowling <mtdowling@gmail.com> and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
web/system/vendor/Cron/MinutesField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
namespace Cron;
/**
* Minutes field. Allows: * , / -
*/
class MinutesField extends AbstractField
{
public function isSatisfiedBy(\DateTime $date, $value)
{
return $this->isSatisfied($date->format('i'), $value);
}
public function increment(\DateTime $date, $invert = false)
{
if ($invert) {
$date->modify('-1 minute');
} else {
$date->modify('+1 minute');
}
return $this;
}
public function validate($value)
{
return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
}
}
web/system/vendor/Cron/MonthField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php
namespace Cron;
use DateTime;
/**
* Month field. Allows: * , / -
*/
class MonthField extends AbstractField
{
public function isSatisfiedBy(DateTime $date, $value)
{
// Convert text month values to integers
$value = str_ireplace(
array(
'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'
),
range(1, 12),
$value
);
return $this->isSatisfied($date->format('m'), $value);
}
public function increment(DateTime $date, $invert = false)
{
if ($invert) {
$date->modify('last day of previous month');
$date->setTime(23, 59);
} else {
$date->modify('first day of next month');
$date->setTime(0, 0);
}
return $this;
}
public function validate($value)
{
return (bool) preg_match('/^[\*,\/\-0-9A-Z]+$/', $value);
}
}
web/system/vendor/Cron/YearField.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
namespace Cron;
/**
* Year field. Allows: * , / -
*/
class YearField extends AbstractField
{
public function isSatisfiedBy(\DateTime $date, $value)
{
return $this->isSatisfied($date->format('Y'), $value);
}
public function increment(\DateTime $date, $invert = false)
{
if ($invert) {
$date->modify('-1 year');
$date->setDate($date->format('Y'), 12, 31);
$date->setTime(23, 59, 0);
} else {
$date->modify('+1 year');
$date->setDate($date->format('Y'), 1, 1);
$date->setTime(0, 0, 0);
}
return $this;
}
public function validate($value)
{
return (bool) preg_match('/^[\*,\/\-0-9]+$/', $value);
}
}
web/system/vendor/cron.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
if (defined('CRON_EXPRESSION_INIT')) {
return;
}
define('CRON_EXPRESSION_INIT', true);
// Load in dependency maps
require dirname(__FILE__).'/Cron/FieldInterface.php';
require dirname(__FILE__).'/Cron/AbstractField.php';
require dirname(__FILE__).'/Cron/DayOfWeekField.php';
require dirname(__FILE__).'/Cron/HoursField.php';
require dirname(__FILE__).'/Cron/YearField.php';
require dirname(__FILE__).'/Cron/CronExpression.php';
require dirname(__FILE__).'/Cron/FieldFactory.php';
require dirname(__FILE__).'/Cron/MinutesField.php';
require dirname(__FILE__).'/Cron/DayOfMonthField.php';
require dirname(__FILE__).'/Cron/MonthField.php';
// Onice Everything is loaded Can use CronExpression as Document
?>

Archive Download the corresponding diff file

Branches

Number of commits:
Page rendered in 0.16205s using 14 queries.