]> git.mxchange.org Git - friendica.git/blob - src/BaseModule.php
Merge remote-tracking branch 'upstream/develop' into logging
[friendica.git] / src / BaseModule.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica;
23
24 use Friendica\Core\Logger;
25 use Friendica\Model\User;
26
27 /**
28  * All modules in Friendica should extend BaseModule, although not all modules
29  * need to extend all the methods described here
30  *
31  * The filename of the module in src/Module needs to match the class name
32  * exactly to make the module available.
33  *
34  * @author Hypolite Petovan <hypolite@mrpetovan.com>
35  */
36 abstract class BaseModule
37 {
38         /**
39          * Initialization method common to both content() and post()
40          *
41          * Extend this method if you need to do any shared processing before both
42          * content() or post()
43          */
44         public static function init(array $parameters = [])
45         {
46         }
47
48         /**
49          * Module GET method to display raw content from technical endpoints
50          *
51          * Extend this method if the module is supposed to return communication data,
52          * e.g. from protocol implementations.
53          */
54         public static function rawContent(array $parameters = [])
55         {
56                 // echo '';
57                 // exit;
58         }
59
60         /**
61          * Module GET method to display any content
62          *
63          * Extend this method if the module is supposed to return any display
64          * through a GET request. It can be an HTML page through templating or a
65          * XML feed or a JSON output.
66          *
67          * @return string
68          */
69         public static function content(array $parameters = [])
70         {
71                 $o = '';
72
73                 return $o;
74         }
75
76         /**
77          * Module DELETE method to process submitted data
78          *
79          * Extend this method if the module is supposed to process DELETE requests.
80          * Doesn't display any content
81          */
82         public static function delete(array $parameters = [])
83         {
84         }
85
86         /**
87          * Module PATCH method to process submitted data
88          *
89          * Extend this method if the module is supposed to process PATCH requests.
90          * Doesn't display any content
91          */
92         public static function patch(array $parameters = [])
93         {
94         }
95
96         /**
97          * Module POST method to process submitted data
98          *
99          * Extend this method if the module is supposed to process POST requests.
100          * Doesn't display any content
101          */
102         public static function post(array $parameters = [])
103         {
104                 // DI::baseurl()->redirect('module');
105         }
106
107         /**
108          * Called after post()
109          *
110          * Unknown purpose
111          */
112         public static function afterpost(array $parameters = [])
113         {
114         }
115
116         /**
117          * Module PUT method to process submitted data
118          *
119          * Extend this method if the module is supposed to process PUT requests.
120          * Doesn't display any content
121          */
122         public static function put(array $parameters = [])
123         {
124         }
125
126         /*
127          * Functions used to protect against Cross-Site Request Forgery
128          * The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.
129          * In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
130          * or if the security token is used for ajax-calls that happen several times), but only valid for a certain amount of time (3hours).
131          * The "typename" separates the security tokens of different types of forms. This could be relevant in the following case:
132          *    A security token is used to protect a link from CSRF (e.g. the "delete this profile"-link).
133          *    If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
134          *    Actually, important actions should not be triggered by Links / GET-Requests at all, but sometimes they still are,
135          *    so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
136          */
137         public static function getFormSecurityToken($typename = '')
138         {
139                 $user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
140                 $timestamp = time();
141                 $sec_hash = hash('whirlpool', ($user['guid'] ?? '') . ($user['prvkey'] ?? '') . session_id() . $timestamp . $typename);
142
143                 return $timestamp . '.' . $sec_hash;
144         }
145
146         public static function checkFormSecurityToken($typename = '', $formname = 'form_security_token')
147         {
148                 $hash = null;
149
150                 if (!empty($_REQUEST[$formname])) {
151                         /// @TODO Careful, not secured!
152                         $hash = $_REQUEST[$formname];
153                 }
154
155                 if (!empty($_SERVER['HTTP_X_CSRF_TOKEN'])) {
156                         /// @TODO Careful, not secured!
157                         $hash = $_SERVER['HTTP_X_CSRF_TOKEN'];
158                 }
159
160                 if (empty($hash)) {
161                         return false;
162                 }
163
164                 $max_livetime = 10800; // 3 hours
165
166                 $user = User::getById(DI::app()->getLoggedInUserId(), ['guid', 'prvkey']);
167
168                 $x = explode('.', $hash);
169                 if (time() > (intval($x[0]) + $max_livetime)) {
170                         return false;
171                 }
172
173                 $sec_hash = hash('whirlpool', ($user['guid'] ?? '') . ($user['prvkey'] ?? '') . session_id() . $x[0] . $typename);
174
175                 return ($sec_hash == $x[1]);
176         }
177
178         public static function getFormSecurityStandardErrorMessage()
179         {
180                 return DI::l10n()->t("The form security token was not correct. This probably happened because the form has been opened for too long \x28>3 hours\x29 before submitting it.") . EOL;
181         }
182
183         public static function checkFormSecurityTokenRedirectOnError($err_redirect, $typename = '', $formname = 'form_security_token')
184         {
185                 if (!self::checkFormSecurityToken($typename, $formname)) {
186                         Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
187                         Logger::debug('checkFormSecurityToken failed', ['request' => $_REQUEST]);
188                         notice(self::getFormSecurityStandardErrorMessage());
189                         DI::baseUrl()->redirect($err_redirect);
190                 }
191         }
192
193         public static function checkFormSecurityTokenForbiddenOnError($typename = '', $formname = 'form_security_token')
194         {
195                 if (!self::checkFormSecurityToken($typename, $formname)) {
196                         Logger::notice('checkFormSecurityToken failed: user ' . DI::app()->getLoggedInUserNickname() . ' - form element ' . $typename);
197                         Logger::debug('checkFormSecurityToken failed', ['request' => $_REQUEST]);
198
199                         throw new \Friendica\Network\HTTPException\ForbiddenException();
200                 }
201         }
202
203         protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab)
204         {
205                 $tabs = [
206                         [
207                                 'label' => DI::l10n()->t('All contacts'),
208                                 'url'   => $baseUrl . '/contacts',
209                                 'sel'   => !$current || $current == 'all' ? 'active' : '',
210                         ],
211                         [
212                                 'label' => DI::l10n()->t('Followers'),
213                                 'url'   => $baseUrl . '/contacts/followers',
214                                 'sel'   => $current == 'followers' ? 'active' : '',
215                         ],
216                         [
217                                 'label' => DI::l10n()->t('Following'),
218                                 'url'   => $baseUrl . '/contacts/following',
219                                 'sel'   => $current == 'following' ? 'active' : '',
220                         ],
221                         [
222                                 'label' => DI::l10n()->t('Mutual friends'),
223                                 'url'   => $baseUrl . '/contacts/mutuals',
224                                 'sel'   => $current == 'mutuals' ? 'active' : '',
225                         ],
226                 ];
227
228                 if ($displayCommonTab) {
229                         $tabs[] = [
230                                 'label' => DI::l10n()->t('Common'),
231                                 'url'   => $baseUrl . '/contacts/common',
232                                 'sel'   => $current == 'common' ? 'active' : '',
233                         ];
234                 }
235
236                 return $tabs;
237         }
238 }