// Copyright 2016 The Gogs Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package models import ( "fmt" "io" "io/ioutil" "mime/multipart" "os" "os/exec" "path" "path/filepath" "time" "github.com/Unknwon/com" gouuid "github.com/satori/go.uuid" "code.gitea.io/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" ) // ___________ .___.__ __ ___________.__.__ // \_ _____/ __| _/|__|/ |_ \_ _____/|__| | ____ // | __)_ / __ | | \ __\ | __) | | | _/ __ \ // | \/ /_/ | | || | | \ | | |_\ ___/ // /_______ /\____ | |__||__| \___ / |__|____/\___ > // \/ \/ \/ \/ // discardLocalRepoBranchChanges discards local commits/changes of // given branch to make sure it is even to remote branch. func discardLocalRepoBranchChanges(localPath, branch string) error { if !com.IsExist(localPath) { return nil } // No need to check if nothing in the repository. if !git.IsBranchExist(localPath, branch) { return nil } refName := "origin/" + branch if err := git.ResetHEAD(localPath, true, refName); err != nil { return fmt.Errorf("git reset --hard %s: %v", refName, err) } return nil } // DiscardLocalRepoBranchChanges discards the local repository branch changes func (repo *Repository) DiscardLocalRepoBranchChanges(branch string) error { return discardLocalRepoBranchChanges(repo.LocalCopyPath(), branch) } // checkoutNewBranch checks out to a new branch from the a branch name. func checkoutNewBranch(repoPath, localPath, oldBranch, newBranch string) error { if err := git.Checkout(localPath, git.CheckoutOptions{ Timeout: time.Duration(setting.Git.Timeout.Pull) * time.Second, Branch: newBranch, OldBranch: oldBranch, }); err != nil { return fmt.Errorf("git checkout -b %s %s: %v", newBranch, oldBranch, err) } return nil } // CheckoutNewBranch checks out a new branch func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error { return checkoutNewBranch(repo.RepoPath(), repo.LocalCopyPath(), oldBranch, newBranch) } // UpdateRepoFileOptions holds the repository file update options type UpdateRepoFileOptions struct { LastCommitID string OldBranch string NewBranch string OldTreeName string NewTreeName string Message string Content string IsNewFile bool } // UpdateRepoFile adds or updates a file in repository. func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) } if opts.OldBranch != opts.NewBranch { if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) } } localPath := repo.LocalCopyPath() oldFilePath := path.Join(localPath, opts.OldTreeName) filePath := path.Join(localPath, opts.NewTreeName) dir := path.Dir(filePath) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return fmt.Errorf("Failed to create dir %s: %v", dir, err) } // If it's meant to be a new file, make sure it doesn't exist. if opts.IsNewFile { if com.IsExist(filePath) { return ErrRepoFileAlreadyExist{filePath} } } // Ignore move step if it's a new file under a directory. // Otherwise, move the file when name changed. if com.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName { if err = git.MoveFile(localPath, opts.OldTreeName, opts.NewTreeName); err != nil { return fmt.Errorf("git mv %s %s: %v", opts.OldTreeName, opts.NewTreeName, err) } } if err = ioutil.WriteFile(filePath, []byte(opts.Content), 0666); err != nil { return fmt.Errorf("WriteFile: %v", err) } if err = git.AddChanges(localPath, true); err != nil { return fmt.Errorf("git add --all: %v", err) } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ Committer: doer.NewGitSig(), Message: opts.Message, }); err != nil { return fmt.Errorf("CommitChanges: %v", err) } else if err = git.Push(localPath, git.PushOptions{ Remote: "origin", Branch: opts.NewBranch, }); err != nil { return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) } gitRepo, err := git.OpenRepository(repo.RepoPath()) if err != nil { log.Error(4, "OpenRepository: %v", err) return nil } commit, err := gitRepo.GetBranchCommit(opts.NewBranch) if err != nil { log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) return nil } // Simulate push event. oldCommitID := opts.LastCommitID if opts.NewBranch != opts.OldBranch { oldCommitID = git.EmptySHA } if err = repo.GetOwner(); err != nil { return fmt.Errorf("GetOwner: %v", err) } err = PushUpdate( opts.NewBranch, PushUpdateOptions{ PusherID: doer.ID, PusherName: doer.Name, RepoUserName: repo.Owner.Name, RepoName: repo.Name, RefFullName: git.BranchPrefix + opts.NewBranch, OldCommitID: oldCommitID, NewCommitID: commit.ID.String(), }, ) if err != nil { return fmt.Errorf("PushUpdate: %v", err) } UpdateRepoIndexer(repo) return nil } // GetDiffPreview produces and returns diff result of a file which is not yet committed. func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *Diff, err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil { return nil, fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", branch, err) } else if err = repo.UpdateLocalCopyBranch(branch); err != nil { return nil, fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", branch, err) } localPath := repo.LocalCopyPath() filePath := path.Join(localPath, treePath) dir := filepath.Dir(filePath) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return nil, fmt.Errorf("Failed to create dir %s: %v", dir, err) } if err = ioutil.WriteFile(filePath, []byte(content), 0666); err != nil { return nil, fmt.Errorf("WriteFile: %v", err) } cmd := exec.Command("git", "diff", treePath) cmd.Dir = localPath cmd.Stderr = os.Stderr stdout, err := cmd.StdoutPipe() if err != nil { return nil, fmt.Errorf("StdoutPipe: %v", err) } if err = cmd.Start(); err != nil { return nil, fmt.Errorf("Start: %v", err) } pid := process.GetManager().Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd) defer process.GetManager().Remove(pid) diff, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, stdout) if err != nil { return nil, fmt.Errorf("ParsePatch: %v", err) } if err = cmd.Wait(); err != nil { return nil, fmt.Errorf("Wait: %v", err) } return diff, nil } // ________ .__ __ ___________.__.__ // \______ \ ____ | | _____/ |_ ____ \_ _____/|__| | ____ // | | \_/ __ \| | _/ __ \ __\/ __ \ | __) | | | _/ __ \ // | ` \ ___/| |_\ ___/| | \ ___/ | \ | | |_\ ___/ // /_______ /\___ >____/\___ >__| \___ > \___ / |__|____/\___ > // \/ \/ \/ \/ \/ \/ // // DeleteRepoFileOptions holds the repository delete file options type DeleteRepoFileOptions struct { LastCommitID string OldBranch string NewBranch string TreePath string Message string } // DeleteRepoFile deletes a repository file func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) { repoWorkingPool.CheckIn(com.ToStr(repo.ID)) defer repoWorkingPool.CheckOut(com.ToStr(repo.ID)) if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil { return fmt.Errorf("DiscardLocalRepoBranchChanges [branch: %s]: %v", opts.OldBranch, err) } else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil { return fmt.Errorf("UpdateLocalCopyBranch [branch: %s]: %v", opts.OldBranch, err) } if opts.OldBranch != opts.NewBranch { if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil { return fmt.Errorf("CheckoutNewBranch [old_branch: %s, new_branch: %s]: %v", opts.OldBranch, opts.NewBranch, err) } } localPath := repo.LocalCopyPath() if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil { return fmt.Errorf("Remove: %v", err) } if err = git.AddChanges(localPath, true); err != nil { return fmt.Errorf("git add --all: %v", err) } else if err = git.CommitChanges(localPath, git.CommitChangesOptions{ Committer: doer.NewGitSig(), Message: opts.Message, }); err != nil { return fmt.Errorf("CommitChanges: %v", err) } else if err = git.Push(localPath, git.PushOptions{ Remote: "origin", Branch: opts.NewBranch, }); err != nil { return fmt.Errorf("git push origin %s: %v", opts.NewBranch, err) } gitRepo, err := git.OpenRepository(repo.RepoPath()) if err != nil { log.Error(4, "OpenRepository: %v", err) return nil } commit, err := gitRepo.GetBranchCommit(opts.NewBranch) if err != nil { log.Error(4, "GetBranchCommit [branch: %s]: %v", opts.NewBranch, err) return nil } // Simulate push event. oldCommitID := opts.LastCommitID if opts.NewBranch != opts.OldBranch { oldCommitID = git.EmptySHA } if err = repo.GetOwner(); err != nil { return fmt.Errorf("GetOwner: %v", err) } err = PushUpdate( opts.NewBranch, PushUpdateOptions{ PusherID: doer.ID, PusherName: doer.Name, RepoUserName: repo.Owner.Name, RepoName: repo.Name, RefFullName: git.BranchPrefix + opts.NewBranch, OldCommitID: oldCommitID, NewCommitID: commit.ID.String(), }, ) if err != nil { return fmt.Errorf("PushUpdate: %v", err) } return nil } // ____ ___ .__ .___ ___________.___.__ // | | \______ | | _________ __| _/ \_ _____/| | | ____ ______ // | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/ // | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \ // |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ > // |__| \/ \/ \/
/*-
 * Copyright 2016 Vsevolod Stakhov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef SRC_LIBSERVER_FUZZY_BACKEND_H_
#define SRC_LIBSERVER_FUZZY_BACKEND_H_

#include "config.h"
#include "contrib/libev/ev.h"
#include "fuzzy_wire.h"

#ifdef  __cplusplus
extern "C" {
#endif

struct rspamd_fuzzy_backend;
struct rspamd_config;

/*
 * Callbacks for fuzzy methods
 */
typedef void (*rspamd_fuzzy_check_cb) (struct rspamd_fuzzy_reply *rep, void *ud);

typedef void (*rspamd_fuzzy_update_cb) (gboolean success,
										guint nadded,
										guint ndeleted,
										guint nextended,
										guint nignored,
										void *ud);

typedef void (*rspamd_fuzzy_version_cb) (guint64 rev, void *ud);

typedef void (*rspamd_fuzzy_count_cb) (guint64 count, void *ud);

typedef gboolean (*rspamd_fuzzy_periodic_cb) (void *ud);

/**
 * Open fuzzy backend
 * @param ev_base
 * @param config
 * @param err
 * @return
 */
struct rspamd_fuzzy_backend *rspamd_fuzzy_backend_create (struct ev_loop *ev_base,
														  const ucl_object_t *config,
														  struct rspamd_config *cfg,
														  GError **err);


/**
 * Check a specific hash in storage
 * @param cmd
 * @param cb
 * @param ud
 */
void rspamd_fuzzy_backend_check (struct rspamd_fuzzy_backend *bk,
								 const struct rspamd_fuzzy_cmd *cmd,
								 rspamd_fuzzy_check_cb cb, void *ud);

/**
 * Process updates for a specific queue
 * @param bk
 * @param updates queue of struct fuzzy_peer_cmd
 * @param src
 */
void rspamd_fuzzy_backend_process_updates (struct rspamd_fuzzy_backend *bk,
										   GArray *updates, const gchar *src, rspamd_fuzzy_update_cb cb,
										   void *ud);

/**
 * Gets number of hashes from the backend
 * @param bk
 * @param cb
 * @param ud
 */
void rspamd_fuzzy_backend_count (struct rspamd_fuzzy_backend *bk,
								 rspamd_fuzzy_count_cb cb, void *ud);

/**
 * Returns number of revision for a specific source
 * @param bk
 * @param src
 * @param cb
 * @param ud
 */
void rspamd_fuzzy_backend_version (struct rspamd_fuzzy_backend *bk,
								   const gchar *src,
								   rspamd_fuzzy_version_cb cb, void *ud);

/**
 * Returns unique id for backend
 * @param backend
 * @return
 */
const gchar *rspamd_fuzzy_backend_id (struct rspamd_fuzzy_backend *backend);

/**
 * Starts expire process for the backend
 * @param backend
 */
void rspamd_fuzzy_backend_start_update (struct rspamd_fuzzy_backend *backend,
										gdouble timeout,
										rspamd_fuzzy_periodic_cb cb,
										void *ud);

struct ev_loop *rspamd_fuzzy_backend_event_base (struct rspamd_fuzzy_backend *backend);

gdouble rspamd_fuzzy_backend_get_expire (struct rspamd_fuzzy_backend *backend);

/**
 * Closes backend
 * @param backend
 */
void rspamd_fuzzy_backend_close (struct rspamd_fuzzy_backend *backend);

#ifdef  __cplusplus
}
#endif

#endif /* SRC_LIBSERVER_FUZZY_BACKEND_H_ */