Passed
Push — master ( 96d9a7...8f1693 )
by Stefan
23:30 queued 08:38
created

Logopath::weNeedToTalk()   D

Complexity

Conditions 9
Paths 25

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 17
c 2
b 0
f 0
nc 25
nop 0
dl 0
loc 31
rs 4.909
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2017 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
namespace core\diag;
13
14
/**
15
 * This class evaluates the evidence of previous Telepath and/or Sociopath runs
16
 * and figures out whom to send emails to, and with that content. It then sends
17
 * these emails.
18
 */
19
class Logopath extends AbstractTest {
20
21
    /**
22
     * storing the end user's email, if he has given it to us
23
     * @var string|FALSE
24
     */
25
    private $userEmail;
26
27
    /**
28
     * maybe the user has some additional evidence directly on his device?
29
     * @var string|FALSE
30
     */
31
    private $additionalScreenshot;
32
33
    /**
34
     * the list of mails to send
35
     * @var array
36
     */
37
    private $mailStack;
38
39
    const EDUROAM_OT = 0;
40
    const NRO_IDP = 1;
41
    const NRO_SP = 2;
42
    const IDP_PUBLIC = 3;
43
    const IDP_PRIVATE = 4;
44
    const SP = 5;
45
    const ENDUSER = 6;
46
47
    /** we start all our mails with a common prefix, internationalised
48
     *
49
     * @var string
50
     */
51
    private $subjectPrefix;
52
53
    /** and we end with a greeting/disclaimer
54
     *
55
     * @var string
56
     */
57
    private $finalGreeting;
58
59
    /**
60
     * We need to vet user inputs.
61
     * @var \web\lib\common\InputValidation
62
     */
63
    private $validatorInstance;
64
65
    /**
66
     * will be filled with the exact emails to send, by determineMailsToSend()
67
     * @var array
68
     */
69
    private $mailQueue;
70
71
    /**
72
     *
73
     * @var array
74
     */
75
    private $concreteRecipients;
76
77
// cases to consider
78
    const IDP_EXISTS_BUT_NO_DATABASE = 100;
79
80
    /**
81
     * initialise the class: maintain state of existing evidence, and get translated versions of email texts etc.
82
     */
83
    public function __construct() {
84
        parent::__construct();
85
        $this->userEmail = FALSE;
86
        $this->additionalScreenshot = FALSE;
87
88
        $this->mailQueue = [];
89
        $this->concreteRecipients = [];
90
91
        $this->validatorInstance = new \web\lib\common\InputValidation();
92
93
        $this->possibleFailureReasons = $_SESSION["SUSPECTS"] ?? []; // if we know nothing, don't talk to anyone
94
        $this->additionalFindings = $_SESSION["EVIDENCE"] ?? [];
95
96
        $this->subjectPrefix = _("[eduroam Diagnostics]") . " ";
97
        $this->finalGreeting = "\n"
98
                . _("(This service is in an early stage. We apologise if this is a false alert. If this is the case, please send an email report to [email protected], forwarding the entire message (including the 'SUSPECTS' and 'EVIDENCE' data at the end), and explain why this is a false positive.)")
99
                . "\n"
100
                . _("Yours sincerely,") . "\n"
101
                . "\n"
102
                . _("The eduroam diagnostics algorithms");
103
104
        $this->mailStack = [
105
            Logopath::IDP_EXISTS_BUT_NO_DATABASE => [
106
                "to" => [Logopath::NRO_IDP],
107
                "cc" => [Logopath::EDUROAM_OT],
108
                "bcc" => [],
109
                "reply-to" => [Logopath::EDUROAM_OT],
110
                "subject" => _("[POLICYVIOLATION NATIONAL] IdP with no entry in eduroam database"),
111
                "body" => _("Dear NRO administrator,") . "\n"
112
                . "\n"
113
                . wordwrap(sprintf(_("an end-user requested diagnostics for realm %s. Real-time connectivity checks determined that the realm exists, but we were unable to find an IdP with that realm in the eduroam database."), "foo.bar")) . "\n"
114
                . "\n"
115
                . _("By not listing IdPs in the eduroam database, you are violating the eduroam policy.") . "\n"
116
                . "\n"
117
                . _("Additionally, this creates operational issues. In particular, we are unable to direct end users to their IdP for further diagnosis/instructions because there are no contact points for that IdP in the database.") . "\n"
118
                . "\n"
119
                . "Please stop the policy violation ASAP by listing the IdP which is associated to this realm.",
120
            ],
121
        ];
122
    }
123
124
    /**
125
     * if the system asked the user for his email and he's willing to give it to
126
     * us, store it with this function
127
     * 
128
     * @param string $userEmail
129
     */
130
    public function addUserEmail($userEmail) {
131
// returns FALSE if it was not given or bogus, otherwise stores this as mail target
132
        $this->userEmail = $this->validatorInstance->email($userEmail);
133
    }
134
135
    /**
136
     * if the system asked the user for a screenshot and he's willing to give one
137
     * to us, store it with this function
138
     * 
139
     * @param string $binaryData
140
     */
141
    public function addScreenshot($binaryData) {
142
        if ($this->validatorInstance->image($binaryData) === TRUE) {
143
            $imagick = new \Imagick();
144
            $imagick->readimageblob($binaryData);
145
            $imagick->setimageformat("png");
146
            $this->additionalScreenshot = $imagick->getimageblob();
147
        }
148
    }
149
150
    /**
151
     * looks at probabilities and evidence, and decides which mail scenario(s) to send
152
     */
153
    public function determineMailsToSend() {
154
        $this->mailQueue = [];
155
// check for IDP_EXISTS_BUT_NO_DATABASE
156
        if (!in_array(AbstractTest::INFRA_NONEXISTENTREALM, $this->possibleFailureReasons) && $this->additionalFindings[AbstractTest::INFRA_NONEXISTENTREALM]['DATABASE_STATUS']['ID2'] < 0) {
157
            $this->mailQueue[] = Logopath::IDP_EXISTS_BUT_NO_DATABASE;
158
        }
159
160
// after collecting all the conditions, find the target entities in all
161
// the mails, and check if they resolve to a known mail address. If they
162
// do not, this triggers more mails about missing contact info.
163
164
        $abstractRecipients = [];
165
        foreach ($this->mailQueue as $mail) {
166
            $abstractRecipients = array_unique(array_merge($this->mailStack[$mail]['to'], $this->mailStack[$mail]['cc'], $this->mailStack[$mail]['bcc'], $this->mailStack[$mail]['reply-to']));
167
        }
168
// who are those guys? Here is significant legwork in terms of DB lookup
169
        $this->concreteRecipients = [];
170
        foreach ($abstractRecipients as $oneRecipient) {
171
            switch ($oneRecipient) {
172
                case Logopath::EDUROAM_OT:
173
                    $this->concreteRecipients[Logopath::EDUROAM_OT] = ["[email protected]"];
174
                    break;
175
                case Logopath::ENDUSER:
176
// will be filled when sending, from $this->userEmail
177
// hence the +1 below
178
                    break;
179
                case Logopath::IDP_PUBLIC: // intentional fall-through, we populate both in one go
180
                case Logopath::IDP_PRIVATE:
181
                    // CAT contacts, if existing
182
                    if ($this->additionalFindings['INFRA_NONEXISTENT_REALM']['DATABASE_STATUS']['ID1'] > 0) {
183
                        $profile = \core\ProfileFactory::instantiate($this->additionalFindings['INFRA_NONEXISTENT_REALM']['DATABASE_STATUS']['ID1']);
184
185
                        foreach ($profile->getAttributes("support:email") as $oneMailAddress) {
186
                            // CAT contacts are always public
187
                            $this->concreteRecipients[Logopath::IDP_PUBLIC][] = $oneMailAddress;
188
                        }
189
                    }
190
                    // DB contacts, if existing
191
                    if ($this->additionalFindings['INFRA_NONEXISTENT_REALM']['DATABASE_STATUS']['ID2'] > 0) {
192
                        $cat = new \core\CAT();
193
                        $info = $cat->getExternalDBEntityDetails($this->additionalFindings['INFRA_NONEXISTENT_REALM']['DATABASE_STATUS']['ID2']);
194
                        foreach ($info['admins'] as $infoElement) {
195
                            if (isset($infoElement['email'])) {
196
                                // until DB Spec 2.0 is out and used, consider all DB contacts as private
197
                                $this->concreteRecipients[Logopath::IDP_PRIVATE][] = $infoElement['email'];
198
                            }
199
                        }
200
                    }
201
                    break;
202
                case Logopath::NRO_IDP: // same code for both, fall through
203
                case Logopath::NRO_SP:
204
                    $target = ($oneRecipient == Logopath::NRO_IDP ? $this->additionalFindings['INFRA_NRO_IdP'] : $this->additionalFindings['INFRA_NRO_SP']);
205
                    $fed = new \core\Federation($target);
206
                    $adminList = $fed->listFederationAdmins();
0 ignored issues
show
Unused Code introduced by
$adminList is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
207
                    // TODO: we only have those who are signed up for CAT currently, and by their ePTID.
208
                    // in touch with OT to get all, so that we can get a list of emails
209
                    break;
210
                case Logopath::SP:
211
                    // TODO: needs a DB view on SPs in eduroam DB, in touch with OT
212
                    break;
213
            }
214
        }
215
// now see if we lack pertinent recipient info, and add corresponding
216
// mails to the list
217
        if (count($abstractRecipients) != count($this->concreteRecipients) + 1) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
218
            // there is a discrepancy, do something ...
219
            // we need to add a mail to the next higher hierarchy level as escalation
220
            // but may also have to remove the lower one because we don't know the guy.
221
        }
222
    }
223
224
    /**
225
     * sees if it is useful to ask the user for his contact details or screenshots
226
     * @return bool
227
     */
228
    public function isEndUserContactUseful() {
229
        $contactUseful = FALSE;
230
        $this->determineMailsToSend();
231
        foreach ($this->mailQueue as $oneMail) {
232
            if (in_array(Logopath::ENDUSER, $this->mailStack[$oneMail]['to']) ||
233
                    in_array(Logopath::ENDUSER, $this->mailStack[$oneMail]['cc']) ||
234
                    in_array(Logopath::ENDUSER, $this->mailStack[$oneMail]['bcc']) ||
235
                    in_array(Logopath::ENDUSER, $this->mailStack[$oneMail]['reply-to'])) {
236
                $contactUseful = TRUE;
237
            }
238
        }
239
        return $contactUseful;
240
    }
241
242
    const CATEGORYBINDING = ['to' => 'addAddress', 'cc' => 'addCC', 'bcc' => 'addBCC', 'reply-to' => 'addReplyTo'];
243
244
    /**
245
     * sends the mails. Only call this after either determineMailsToSend() or
246
     * isEndUserContactUseful(), otherwise it will do nothing.
247
     */
248
    public function weNeedToTalk() {
249
        foreach ($this->mailQueue as $oneMail) {
250
            $theMail = $this->mailStack[$oneMail];
251
            // if user interaction would have been good, but the user didn't 
252
            // leave his mail address, remove him/her from the list of recipients
253
            foreach (Logopath::CATEGORYBINDING as $index => $functionName) {
254
                if (in_array(Logopath::ENDUSER, $theMail[$index]) && $this->userEmail === FALSE) {
255
                    $theMail[$index] = array_diff($theMail[$index], [Logopath::ENDUSER]);
256
                }
257
            }
258
259
            $handle = \core\common\OutsideComm::mailHandle();
260
            // let's identify outselves
261
            $handle->FromName = CONFIG['APPEARANCE']['productname'] . " Real-Time Diagnostics System";
262
            // add recipients
263
            foreach (Logopath::CATEGORYBINDING as $arrayName => $functionName) {
264
                foreach ($theMail[$arrayName] as $onePrincipal) {
265
                    foreach ($this->concreteRecipients[$onePrincipal] as $oneConcrete) {
266
                        $handle->{$functionName}($oneConcrete);
267
                    }
268
                }
269
            }
270
            // and add what to say
271
            $handle->Subject = $theMail['subject'];
272
            $handle->Body = $theMail['body'];
273
            if ($this->additionalScreenshot !== FALSE) {
274
                $handle->addStringAttachment($this->additionalScreenshot, "screenshot.png", "base64", "image/png", "attachment");
275
            }
276
            $handle->send();
277
        }
278
    }
279
280
}
281