Example #1
0
 public function scopeWhereAuthorIP($query, $ip)
 {
     $ip = new IP($ip);
     return $query->where('author_ip', $ip->toSQL());
 }
Example #2
0
 /**
  * Generates a snapshot in the database for the previous hour.
  *
  * @param  \Carbon\Carbon  $carbon  A timestamp within the 0-60 minute block that is to be snapshotted.
  * @return array  of new \App\Stats
  */
 public function createStatsSnapshot(\Carbon\Carbon $carbon)
 {
     $carbonStart = $carbon->minute(0)->second(0);
     $carbonEnd = clone $carbonStart;
     $carbonEnd = $carbonEnd->addHour()->minute(0)->second(0)->subSecond();
     $posts = $this->posts()->withTrashed()->where('created_at', '>=', $carbonStart)->where('created_at', '<=', $carbonEnd)->select('post_id', 'author_ip', 'reply_to')->get();
     if ($posts->count() === 0) {
         return collect([]);
     }
     // Unique IPs.
     $authorsUnique = [];
     // Unique Post IDs.
     $postsUnique = [];
     // Unique \16 ranges.
     $rangesUnique = [];
     // Unique Thread Post IDs.
     $threadsUnique = [];
     foreach ($posts as $post) {
         $postsUnique[$post->post_id] = true;
         if (is_null($post->reply_to)) {
             $threadsUnique[$post->post_id] = false;
         }
         if (!is_null($post->author_ip)) {
             $ip = new IP($post->author_ip);
             if (!isset($authorsUnique[$ip->toText()])) {
                 $authorsUnique[$ip->toText()] = $ip->toLong();
                 $range = new IP("{$ip->getStart()}/16");
                 $rangesUnique[$range->getStart()] = $range->toLong();
             }
         }
     }
     // Save uniques
     $statsRows = [];
     $uniques = ['authors' => array_values($authorsUnique), 'posts' => array_keys($postsUnique), 'ranges' => array_values($rangesUnique), 'threads' => array_keys($threadsUnique)];
     foreach ($uniques as $statsKey => $uniqueValues) {
         $statsBits = [];
         foreach ($uniqueValues as $uniqueValue) {
             $statsBits[] = ['unique' => (int) $uniqueValue];
         }
         $statsRow = $this->stats()->updateOrCreate(['stats_time' => $carbonStart, 'stats_type' => $statsKey], ['counter' => count($statsBits)]);
         if (!$statsRow->exists) {
             $statsRow->save();
         }
         $statsRow->uniques()->createMany($statsBits);
         $statsRows[] = $statsRow;
     }
     return collect($statsRows);
 }
 /**
  *
  */
 public function putMod(Request $request, Board $board, Post $post)
 {
     if (!$post->exists) {
         abort(404);
     }
     // Take trailing arguments,
     // compare them against a list of real actions,
     // intersect the liss to find the true commands.
     $actions = ["delete", "ban", "all", "global"];
     $argList = func_get_args();
     $modActions = array_intersect($actions, array_splice($argList, 2));
     sort($modActions);
     $ban = in_array("ban", $modActions);
     $delete = in_array("delete", $modActions);
     $all = in_array("all", $modActions);
     $global = in_array("global", $modActions);
     if (!$ban) {
         return abort(404);
     }
     $validator = Validator::make(Input::all(), ['raw_ip' => 'required|boolean', 'ban_ip' => 'required_if:raw_ip,true|ip', 'ban_ip_range' => 'required|between:0,128', 'justification' => 'max:255', 'expires_days' => 'required|integer|min:0|max:' . $this->option('banMaxLength'), 'expires_hours' => 'required|integer|min:0|max:23', 'expires_minutes' => 'required|integer|min:0|max:59']);
     if (!$validator->passes()) {
         return redirect()->back()->withInput(Input::all())->withErrors($validator->errors());
     }
     $banLengthStr = [];
     $expiresDays = Input::get('expires_days');
     $expiresHours = Input::get('expires_hours');
     $expiresMinutes = Input::get('expires_minutes');
     if ($expiresDays > 0) {
         $banLengthStr[] = "{$expiresDays}d";
     }
     if ($expiresHours > 0) {
         $banLengthStr[] = "{$expiresHours}h";
     }
     if ($expiresMinutes > 0) {
         $banLengthStr[] = "{$expiresMinutes}m";
     }
     if ($expiresDays == 0 && $expiresHours == 0 && $expiresMinutes == 0) {
         $banLengthStr[] = "&Oslash;";
     }
     $banLengthStr = implode($banLengthStr, " ");
     // If we're banning without the ability to view IP addresses, we will get our address directly from the post in human-readable format.
     $banIpAddr = $this->user->canViewRawIP() ? Input::get('ban_ip') : $post->getAuthorIpAsString();
     // The CIDR is passed from our post parameters. By default, it is 32/128 for IPv4/IPv6 respectively.
     $banCidr = Input::get('ban_ip_range');
     // This generates a range from start to finish. I.E. 192.168.1.3/22 becomes [192.168.0.0, 192.168.3.255].
     // If we just pass the CDIR into the construct, we get 192.168.1.3-129.168.3.255 for some reason.
     $banCidrRange = IP::cidr_to_range("{$banIpAddr}/{$banCidr}");
     // We then pass this range into the construct method.
     $banIp = new IP($banCidrRange[0], $banCidrRange[1]);
     $ban = new Ban();
     $ban->ban_ip_start = $banIp->getStart();
     $ban->ban_ip_end = $banIp->getEnd();
     $ban->seen = false;
     $ban->created_at = $ban->freshTimestamp();
     $ban->updated_at = clone $ban->created_at;
     $ban->expires_at = clone $ban->created_at;
     $ban->expires_at = $ban->expires_at->addDays($expiresDays);
     $ban->expires_at = $ban->expires_at->addHours($expiresHours);
     $ban->expires_at = $ban->expires_at->addMinutes($expiresMinutes);
     $ban->mod_id = $this->user->user_id;
     $ban->post_id = $post->post_id;
     $ban->ban_reason_id = null;
     $ban->justification = Input::get('justification');
     if ($global) {
         if ($ban && !$this->user->canBanGlobally() || $delete && !$this->user->canDeleteGlobally()) {
             return abort(403);
         }
         if ($ban) {
             $ban->board_uri = null;
             $ban->save();
         }
         $this->log('log.post.ban.global', $post, ["board_id" => $post->board_id, "board_uri" => $post->board_uri, "ip" => $post->getAuthorIpAsString(), "justification" => $ban->justification, "time" => $banLengthStr]);
         if ($delete) {
             $posts = Post::ipBinary($post->author_ip);
             $this->log('log.post.ban.delete', $post, ["board_id" => $post->board_id, "board_uri" => $post->board_uri, "posts" => $posts->count()]);
             $posts->delete();
             return redirect($board->board_uri);
         }
     } else {
         if ($ban && !$board->canBan($this->user) || $delete && !$board->canDelete($this->user)) {
             return abort(403);
         }
         if ($ban) {
             $ban->board_uri = $post->board_uri;
             $ban->save();
         }
         $this->log('log.post.ban.local', $post, ["board_id" => $post->board_id, "board_uri" => $post->board_uri, "ip" => $post->getAuthorIpAsString(), "justification" => $ban->justification, "time" => $banLengthStr]);
         if ($delete) {
             if ($all) {
                 $posts = Post::ipBinary($post->author_ip)->where('board_uri', $board->board_uri);
                 $this->log('log.post.ban.delete', $post, ["board_id" => $post->board_id, "board_uri" => $post->board_uri, "posts" => $posts->count()]);
                 $posts->delete();
                 return redirect($board->board_uri);
             } else {
                 $this->log('log.post.ban.delete', $post, ["board_id" => $post->board_id, "board_uri" => $post->board_uri, "posts" => 1]);
                 $post->delete();
             }
         }
     }
     Event::fire(new PostWasBanned($post));
     Event::fire(new PostWasModerated($post, $this->user));
     return back();
 }
Example #4
0
 public function scopeWhereIPInBan($query, $ip)
 {
     $ip = new IP($ip);
     return $query->where(function ($query) use($ip) {
         $query->where('ban_ip_start', '<=', $ip->toSQL());
         $query->where('ban_ip_end', '>=', $ip->toSQL());
     });
 }
Example #5
0
 /**
  * Uploads a single file.
  *
  * @param  Request  $request
  * @param  Board  $board
  * @return json
  */
 public function putFile(Request $request, Board $board)
 {
     $input = Input::all();
     $rules = [];
     PostRequest::rulesForFiles($board, $rules);
     $rules['files'][] = "required";
     $validator = Validator::make($input, $rules);
     if (!$validator->passes()) {
         return json_encode(['errors' => $validator->errors()]);
     }
     $storage = new Collection();
     foreach ($input['files'] as $file) {
         $ip = new IP($request->ip());
         $uploadSize = (int) Cache::get("upstream_data_for_" . $ip->toLong(), 0);
         if ($uploadSize <= 52430000) {
             Cache::increment("upstream_data_for_" . $ip->toLong(), $file->getSize(), 2);
             $newStorage = FileStorage::storeUpload($file);
             $storage[$newStorage->hash] = $newStorage;
             Cache::decrement("upstream_data_for_" . $ip->toLong(), $file->getSize());
         } else {
             return abort(429);
         }
     }
     return $storage;
 }
Example #6
0
 /**
  * Validate the class instance.
  * This overrides the default invocation to provide additional rules after the controller is setup.
  *
  * @return void
  */
 public function validate()
 {
     $board = $this->board;
     $thread = $this->thread;
     $user = $this->user;
     $ip = new IP($this->ip());
     $carbon = new \Carbon\Carbon();
     $validator = $this->getValidatorInstance();
     $messages = $validator->errors();
     $isReply = $this->thread instanceof Post;
     if ($isReply) {
         $floodTime = site_setting('postFloodTime');
         // Check global flood.
         $nextPostTime = Carbon::createFromTimestamp(Cache::get('last_post_for_' . $ip->toLong(), 0) + $floodTime);
         if ($nextPostTime->isFuture()) {
             $timeDiff = $nextPostTime->diffInSeconds() + 1;
             $messages->add("flood", trans_choice("validation.custom.post_flood", $timeDiff, ['time_left' => $timeDiff]));
             $this->failedValidation($validator);
             return;
         }
     } else {
         $floodTime = site_setting('threadFloodTime');
         // Check global flood.
         $nextPostTime = Carbon::createFromTimestamp(Cache::get('last_thread_for_' . $ip->toLong(), 0) + $floodTime);
         if ($nextPostTime->isFuture()) {
             $timeDiff = $nextPostTime->diffInSeconds() + 1;
             $messages->add("flood", trans_choice("validation.custom.thread_flood", $timeDiff, ['time_left' => $timeDiff]));
             $this->failedValidation($validator);
             return;
         }
     }
     // Board-level setting validaiton.
     $validator->sometimes('captcha', "required|captcha", function ($input) use($board) {
         return !$board->canPostWithoutCaptcha($this->user);
     });
     if (!$validator->passes()) {
         $this->failedValidation($validator);
     } else {
         if (!$this->user->canAdminConfig() && $board->canPostWithoutCaptcha($this->user)) {
             // Check last post time for flood.
             $floodTime = site_setting('postFloodTime');
             if ($floodTime > 0) {
                 $lastPost = Post::getLastPostForIP();
                 if ($lastPost) {
                     $floodTimer = clone $lastPost->created_at;
                     $floodTimer->addSeconds($floodTime);
                     if ($floodTimer->isFuture()) {
                         $messages->add("flood", trans("validation.custom.post_flood", ['time_left' => $floodTimer->diffInSeconds()]));
                         $this->failedValidation($validator);
                         return;
                     }
                 }
             }
         }
         // Validate individual files being uploaded right now.
         $this->validateOriginality();
     }
     if (count($validator->errors())) {
         $this->failedValidation($validator);
     } else {
         if (!$this->passesAuthorization()) {
             $this->failedAuthorization();
         }
     }
 }